; life2.s is an implementation of Conway's Game of Life for Commodore 64 ; Copyright (C) 2009 Tommi Saviranta ; ; This program is free software: you can redistribute it and/or modify ; it under the terms of the GNU General Public License as published by ; the Free Software Foundation, either version 3 of the License, or ; (at your option) any later version. ; ; This program is distributed in the hope that it will be useful, ; but WITHOUT ANY WARRANTY; without even the implied warranty of ; MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the ; GNU General Public License for more details. ; ; You should have received a copy of the GNU General Public License ; along with this program. If not, see . ; 38 * 23 (comparable to life.s) ; ; 1 3.7 s ; 2 7.4 s ; 3 11.1 s ; 4 14.8 s ; 5 18.5 s ; ------------ ; avg 3.70 s ; this source includes following (generated) sources ; life2-bitref.s generated by life2-bitref.pl, bitmask lookup tables ; life2-init.s generated by life2-init.pl, pointer init, border copy ; life2-font.s manually created packed byte font #include "life2-config.s" .cpu "6510" ; basic start * = $0801 .word bas_end .word 2009 ; line number .byte $9e ; sys .pet "2061" ; $0801 + basic length .byte 0 bas_end: .word 0 ; we don't need interrupts lda $dc0e and #254 sta $dc0e sei ; copy common bitref to zeropage (to save whopping 3 cycles per cell) #macro copyzp(from, to) { lda {from}, X sta {to}, X } ldx #$0f zp_copy: .copyzp(bitref_a1, zp_bitref_a1) .copyzp(bitref_a2, zp_bitref_a2) .copyzp(bitref_a3, zp_bitref_a3) .copyzp(bitref_b1, zp_bitref_b1) .copyzp(bitref_b2, zp_bitref_b2) .copyzp(bitref_b4, zp_bitref_b4) .copyzp(bitref_c1, zp_bitref_c1) .copyzp(bitref_c3, zp_bitref_c3) .copyzp(bitref_c4, zp_bitref_c4) .copyzp(bitref_d2, zp_bitref_d2) .copyzp(bitref_d3, zp_bitref_d3) .copyzp(bitref_d4, zp_bitref_d4) lda #1 sta SCREEN_A, X dex bpl zp_copy chars: ; create custom chars ldx #0 char_loop: lda #0 sta CHARRAM, Y iny lda chardata, X sta CHARRAM, Y iny sta CHARRAM, Y iny sta CHARRAM, Y iny inx txa cmp #32 bne char_loop ; switch to custom char set lda $d018 and #240 adc #12 sta $d018 ; initialise stuff lda #0 sta PAGE_PTR init_screen: ; colours lda #BORDER_COLOUR sta SCREEN_BORDER lda #BG_COLOUR sta SCREEN_BG ; clear screens ; lax #0 lda #0 ldx #0 init_screen_0: sta SCREEN_A + 0, X sta SCREEN_A + 256, X sta SCREEN_A + 512, X sta SCREEN_A + 768, X sta SCREEN_B + 0, X sta SCREEN_B + 256, X sta SCREEN_B + 512, X sta SCREEN_B + 768, X inx bne init_screen_0 ; hide borders lda #BG_COLOUR ; horizontal borders (including corners) ldx #(BOARD_WIDTH / 2 + 1) init_screen_1: sta COLOUR, X ; sta SCREEN_A, X sta COLOUR + (BOARD_HEIGHT / 2 + 1) * SCREEN_WIDTH, X ; sta SCREEN_A + (BOARD_HEIGHT / 2 + 1) * SCREEN_WIDTH, X dex bpl init_screen_1 ldx #(BOARD_HEIGHT / 2) init_screen_2: lda #BG_COLOUR init_screen_2a: sta COLOUR + SCREEN_WIDTH init_screen_2b: sta COLOUR + SCREEN_WIDTH + (BOARD_WIDTH / 2) + 1 clc lda init_screen_2a + 1 adc #SCREEN_WIDTH sta init_screen_2a + 1 bcc init_screen_2bi init_screen_2ai: inc init_screen_2a + 2 clc init_screen_2bi: lda init_screen_2b + 1 adc #SCREEN_WIDTH sta init_screen_2b + 1 bcc init_screen_2z inc init_screen_2b + 2 init_screen_2z: dex bne init_screen_2 ; create glider #macro setcoord(byte, base, col, row) { lda {byte} sta {base} + {row} * SCREEN_WIDTH + {col} } .setcoord(#$02, SCREEN_A, 9, 8) .setcoord(#$04, SCREEN_A, 10, 8) .setcoord(#$03, SCREEN_A, 9, 9) .setcoord(#$01, SCREEN_A, 10, 9) ; border wrap patterns ; expects size 64 * 16 ; .setcoord(#$03, SCREEN_A, 3, 1) ; .setcoord(#$0c, SCREEN_A, 3, 8) ; ; .setcoord(#$05, SCREEN_A, 1, 4) ; .setcoord(#$0a, SCREEN_A, 32, 4) ; ; .setcoord(#$01, SCREEN_A, 1, 1) ; .setcoord(#$02, SCREEN_A, 32, 1) ; .setcoord(#$04, SCREEN_A, 1, 8) ; .setcoord(#$08, SCREEN_A, 32, 8) ; play round init_round: ; update page pointer lda PAGE_PTR eor #1 sta PAGE_PTR ; initialise round beq init_round_b_vec init_round_a_vec: jsr init_round_a jmp init_iterate init_round_b_vec: jsr init_round_b init_iterate: lda #BOARD_HEIGHT / 2 sta Y_PTR iterate: ; Y is our cell index -- don't touch it! ldy #BOARD_WIDTH / 2 - 1 clc cell_a: ptr_a1: ldx $ffff, Y ; (-1, -1) 5 lda zp_bitref_a1, X ; 4 ptr_a2: ldx $ffff, Y ; ( 0, -1) 5 adc zp_bitref_a2, X ; 4 ptr_a3: ldx $ffff, Y ; (-1, 0) 5 adc zp_bitref_a3, X ; 4 ptr_a4: ora $ffff, Y ; ( 0, 0) 5 tax ; 2 lda bitref_a, X ; 5 sta TMP0 ; 3 ; ; 45 cell_b: ptr_b1: ldx $ffff, Y ; ( 0, -1) 5 lda zp_bitref_b1, X ; 4 ptr_b2: ldx $ffff, Y ; ( 1, -1) 5 adc zp_bitref_b2, X ; 4 ptr_b4: ldx $ffff, Y ; ( 1, 1) 5 adc zp_bitref_b4, X ; 4 ptr_b3: ora SCREEN_A, Y ; ( 0, 0) 5 tax ; 2 lda bitref_b, X ; 5 ora TMP0 ; 3 sta TMP0 ; 3 ; 48 cell_c: ptr_c1: ldx $ffff, Y ; ( 0, 0) 5 lda zp_bitref_c1, X ; 4 ptr_c3: ldx $ffff, Y ; ( 0, 1) 5 adc zp_bitref_c3, X ; 4 ptr_c4: ldx $ffff, Y ; ( 1, 1) 5 adc zp_bitref_c4, X ; 4 ptr_c2: ora SCREEN_A, Y ; ( 1, 0) 5 tax ; 2 lda bitref_c, X ; 5 ora TMP0 ; 3 sta TMP0 ; 3 ; 48 cell_d: ptr_d2: ldx $ffff, Y ; ( 1, 0) 5 lda zp_bitref_d2, X ; 4 ptr_d3: ldx $ffff, Y ; ( 0, 1) 5 adc zp_bitref_d3, X ; 4 ptr_d4: ldx $ffff, Y ; ( 1, 1) 5 adc zp_bitref_d4, X ; 4 ptr_d1: ora SCREEN_A, Y ; ( 0, 0) 5 tax ; 2 lda bitref_d, X ; 5 ora TMP0 ; 3 ptr_t: sta $ffff, Y ; ( 0, 0) 5 ; 50 dey bpl cell_a #print "iteration loop: ", $80 - (* - cell_a), " bytes remaining" play_round_1_continue: update_pointers: clc #macro updateptr(ptr, next) { lda {ptr} + 1 adc #SCREEN_WIDTH sta {ptr} + 1 bcc {next} inc {ptr} + 2 clc } update_ptr_a1: .updateptr(ptr_a1, update_ptr_a2) update_ptr_a2: .updateptr(ptr_a2, update_ptr_a3) update_ptr_a3: .updateptr(ptr_a3, update_ptr_a4) update_ptr_a4: .updateptr(ptr_a4, update_ptr_b1) update_ptr_b1: .updateptr(ptr_b1, update_ptr_b2) update_ptr_b2: .updateptr(ptr_b2, update_ptr_b3) update_ptr_b3: .updateptr(ptr_b3, update_ptr_b4) update_ptr_b4: .updateptr(ptr_b4, update_ptr_c1) update_ptr_c1: .updateptr(ptr_c1, update_ptr_c2) update_ptr_c2: .updateptr(ptr_c2, update_ptr_c3) update_ptr_c3: .updateptr(ptr_c3, update_ptr_c4) update_ptr_c4: .updateptr(ptr_c4, update_ptr_d1) update_ptr_d1: .updateptr(ptr_d1, update_ptr_d2) update_ptr_d2: .updateptr(ptr_d2, update_ptr_d3) update_ptr_d3: .updateptr(ptr_d3, update_ptr_d4) update_ptr_d4: .updateptr(ptr_d4, update_ptr_t) update_ptr_t: .updateptr(ptr_t, update_ptr_done) update_ptr_done: dec Y_PTR beq next_round jmp iterate next_round: jmp init_round fail: stx $2000 lda #2 sta SCREEN_BORDER sta SCREEN_BG rts ; screen initialisation, generated by life2-init.pl ; - set pointers ; - copy borders #include "life2-init.s" ; reference tables, generated by life2-bitref.pl #include "life2-bitref.s" ; packed bits font #include "life2-font.s" ; constants TMP0 = $07 PAGE_PTR = $08 Y_PTR = $09 zp_bitref_a1 = $10 zp_bitref_a2 = $20 zp_bitref_a3 = $30 zp_bitref_b1 = $40 zp_bitref_b2 = $50 zp_bitref_b4 = $60 zp_bitref_c1 = $80 zp_bitref_c3 = $90 zp_bitref_c4 = $a0 zp_bitref_d2 = $b0 zp_bitref_d3 = $c0 zp_bitref_d4 = $d0