; life.s is 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 . ; 1 6.1 s ; 2 12.4 s ; 3 18.6 s ; 4 24.8 s ; 5 31.1 s ; ------------ ; avg 6.22 s processor 6502 seg data org $0801, 0 dc $0b, $08, $d9, $07, $9e, "2061", $00 dc $00, $00 seg code org 2061, 0 cld ; turn off interrupts ; this is NOT needed, but slighly speeds up calculation lda $dc0e and #254 sta $dc0e ; create custom chars ; essentially @ is dead cell, A is a live one lda #0 ldx #8 char_loop_0: sta $3000,X dex bpl char_loop_0 lda #$7f ldx 7 char_loop_1: sta $3009,X dex bpl char_loop_1 ; switch to custom char set lda $d018 and #240 adc #12 sta $d018 ; ; 38 cols ; lda $d016 ; and #$f7 ; sta $d016 ; ; ; 24 rows ; lda $d011 ; and #$f7 ; sta $d011 lda #14 sta SCREEN_BORDER lda #6 sta SCREEN_BG ; initialise screen ; ; set all cells to 0 init_screen: lda #0 ldx #0 init_screen_0: sta SCREEN,X sta SCREEN + 256,X sta SCREEN + 512,X sta SCREEN + 768,X inx bne init_screen_0 ; set cell foreground colour to match screen border lda SCREEN_BORDER ldx #0 init_screen_1: sta COLOUR,X sta COLOUR + 256,X sta COLOUR + 512,X sta COLOUR + 768,X inx bne init_screen_1 ; set wrapper border's foreground colour to match screen background ; top and bottom lda SCREEN_BG ldx #39 init_screen_2: sta COLOUR,X sta COLOUR + 24 * 40,X dex bpl init_screen_2 ; left and right ldx #25 init_screen_3: lda SCREEN_BG init_screen_3a: sta COLOUR init_screen_3b: sta COLOUR + 39 lda init_screen_3a + 1 clc adc #40 sta init_screen_3a + 1 bcc init_screen_3bi init_screen_3ai: inc init_screen_3a + 2 init_screen_3bi: lda init_screen_3b + 1 clc adc #40 sta init_screen_3b + 1 bcc init_screen_3c inc init_screen_3b + 2 init_screen_3c: dex bne init_screen_3 create_screen: lda #1 sta SCREEN + 15 * 40 + 20 sta SCREEN + 16 * 40 + 21 sta SCREEN + 17 * 40 + 19 sta SCREEN + 17 * 40 + 20 sta SCREEN + 17 * 40 + 21 ; sta SCREEN + 10 * 40 + 20 ; sta SCREEN + 10 * 40 + 21 ; sta SCREEN + 11 * 40 + 19 ; sta SCREEN + 11 * 40 + 20 ; sta SCREEN + 12 * 40 + 20 ; ; sta SCREEN + 17 * 40 + 30 ; sta SCREEN + 18 * 40 + 30 ; sta SCREEN + 18 * 40 + 31 copy_to_work: ldx #0 copy_to_work_0: lda SCREEN,X sta NEW_SCREEN,X lda SCREEN + 256,X sta NEW_SCREEN + 256,X lda SCREEN + 512,X sta NEW_SCREEN + 512,X lda SCREEN + 768,X sta NEW_SCREEN + 768,X inx bne copy_to_work_0 iterate: ; initialise world ldx #0 stx y_a jsr copy_borders jsr reset_iter_addr iterate_row: ; X = memory index -- don't modify X inside the main loop! ; iterate each row backwards ; this way "dex; bpl" is enough ; other option would be to use "inx; cpx #38; beq" ldx #37 iterate_cell: ; TODO: can we save cycles by clearing carry only when needed? clc ; count neighbours ; 0, 1, 2, 40, 41, 42... try to stay on the same page ; (as opposed to 0, 40, 80, 1, 41, 81...) col_1a: lda $ffff,X ; overwritten (-1, -1) col_1b: adc $ffff,X ; overwritten ( 0, -1) col_1c: adc $ffff,X ; overwritten ( 1, -1) col_2a: adc $ffff,X ; overwritten (-1, 0) col_2c: adc $ffff,X ; overwritten ( 1, 0) col_3a: adc $ffff,X ; overwritten (-1, 1) col_3b: adc $ffff,X ; overwritten ( 0, 1) ; save 2 cycles almost every time! beq life_or_death col_3c: adc $ffff,X ; overwritten ( 1, 1) ; decide what to do with the cell life_or_death: ; optimise jumps ; 1. cells are most likely to be dead and stay that way ; 2. if cell is alive, it's most likely to die col_2b: ldy $ffff,X ; overwritten ( 0, 0) bne alive_to_what dead_to_what: cmp #3 bne increment lda #1 jmp set_state alive_to_what: cmp #2 beq increment cmp #3 beq increment lda #0 ; fall through set_state: sta $ffff,X ; overwritten ( 0, 0) ; fall through increment: dex bpl iterate_cell clc inc_1a: lda col_1a + 1 adc #40 sta col_1a + 1 bcc inc_2a inc col_1a + 2 clc inc_2a: lda col_2a + 1 adc #40 sta col_2a + 1 bcc inc_3a inc col_2a + 2 clc inc_3a: lda col_3a + 1 adc #40 sta col_3a + 1 bcc inc_1b inc col_3a + 2 clc inc_1b: lda col_1b + 1 adc #40 sta col_1b + 1 bcc inc_2b inc col_1b + 2 clc inc_2b: lda col_2b + 1 adc #40 sta col_2b + 1 bcc inc_3b inc col_2b + 2 clc inc_3b: lda col_3b + 1 adc #40 sta col_3b + 1 bcc inc_1c inc col_3b + 2 clc inc_1c: lda col_1c + 1 adc #40 sta col_1c + 1 bcc inc_2c inc col_1c + 2 clc inc_2c: lda col_2c + 1 adc #40 sta col_2c + 1 bcc inc_3c inc col_2c + 2 clc inc_3c: lda col_3c + 1 adc #40 sta col_3c + 1 bcc inc_set inc col_3c + 2 clc inc_set: lda set_state + 1 adc #40 sta set_state + 1 bcc inc_done inc set_state + 2 clc inc_done: inc y_a ldy y_a cpy #23 beq copy_world jmp iterate_row ; copy work board to screen copy_world: ldx #0 copy_world_a: lda NEW_SCREEN_C,X sta SCREEN_C,X inx bne copy_world_a copy_world_b: lda NEW_SCREEN_C + 256,X sta SCREEN_C + 256,X inx bne copy_world_b copy_world_c: lda NEW_SCREEN_C + 512,X sta SCREEN_C + 512,X inx bne copy_world_c ; Copy remaining 1000 - 256 - 256 - 256 - 41 - 41 = 150 bytes. ; I suppose the fastest way to do that is to decrease X. Since ; we can't use N-flag, we misuse Z-flag. Therefore we have to ; run through indexes 151--1. ldx #151 ; 1000 - 256 - 256 - 256 - 41 - 41 + 1 copy_world_d: lda NEW_SCREEN + 1000 - 150 - 1 - 1 - 1 * 40,X sta SCREEN + 1000 - 150 - 1 - 1 - 1 * 40,X dex bne copy_world_d jmp iterate ; resets neighbour calculation addresses reset_iter_addr: ; 1a ldx #SCREEN_1A stx col_1a + 2 ; 2a ldx #SCREEN_2A stx col_2a + 2 ; 3a ldx #SCREEN_3A stx col_3a + 2 ; 1b ldx #SCREEN_1B stx col_1b + 2 ; 2b ldx #SCREEN_2B stx col_2b + 2 ; 3b ldx #SCREEN_3B stx col_3b + 2 ; 1c ldx #SCREEN_1C stx col_1c + 2 ; 2c ldx #SCREEN_2C stx col_2c + 2 ; 3c ldx #SCREEN_3C stx col_3c + 2 ; set ldx #NEW_SCREEN_C stx set_state + 2 rts ; create wrapper borders copy_borders: ; generated with borders.sh ; corners lda SCREEN + 1 + 1 * 40 sta SCREEN + 39 + 24 * 40 lda SCREEN + 38 + 1 * 40 sta SCREEN + 0 + 24 * 40 lda SCREEN + 1 + 23 * 40 sta SCREEN + 39 + 0 * 40 lda SCREEN + 38 + 23 * 40 sta SCREEN + 0 + 0 * 40 ; horizontal lda SCREEN + 1 + 1 * 40 sta SCREEN + 1 + 24 * 40 lda SCREEN + 1 + 23 * 40 sta SCREEN + 1 + 0 * 40 lda SCREEN + 2 + 1 * 40 sta SCREEN + 2 + 24 * 40 lda SCREEN + 2 + 23 * 40 sta SCREEN + 2 + 0 * 40 lda SCREEN + 3 + 1 * 40 sta SCREEN + 3 + 24 * 40 lda SCREEN + 3 + 23 * 40 sta SCREEN + 3 + 0 * 40 lda SCREEN + 4 + 1 * 40 sta SCREEN + 4 + 24 * 40 lda SCREEN + 4 + 23 * 40 sta SCREEN + 4 + 0 * 40 lda SCREEN + 5 + 1 * 40 sta SCREEN + 5 + 24 * 40 lda SCREEN + 5 + 23 * 40 sta SCREEN + 5 + 0 * 40 lda SCREEN + 6 + 1 * 40 sta SCREEN + 6 + 24 * 40 lda SCREEN + 6 + 23 * 40 sta SCREEN + 6 + 0 * 40 lda SCREEN + 7 + 1 * 40 sta SCREEN + 7 + 24 * 40 lda SCREEN + 7 + 23 * 40 sta SCREEN + 7 + 0 * 40 lda SCREEN + 8 + 1 * 40 sta SCREEN + 8 + 24 * 40 lda SCREEN + 8 + 23 * 40 sta SCREEN + 8 + 0 * 40 lda SCREEN + 9 + 1 * 40 sta SCREEN + 9 + 24 * 40 lda SCREEN + 9 + 23 * 40 sta SCREEN + 9 + 0 * 40 lda SCREEN + 10 + 1 * 40 sta SCREEN + 10 + 24 * 40 lda SCREEN + 10 + 23 * 40 sta SCREEN + 10 + 0 * 40 lda SCREEN + 11 + 1 * 40 sta SCREEN + 11 + 24 * 40 lda SCREEN + 11 + 23 * 40 sta SCREEN + 11 + 0 * 40 lda SCREEN + 12 + 1 * 40 sta SCREEN + 12 + 24 * 40 lda SCREEN + 12 + 23 * 40 sta SCREEN + 12 + 0 * 40 lda SCREEN + 13 + 1 * 40 sta SCREEN + 13 + 24 * 40 lda SCREEN + 13 + 23 * 40 sta SCREEN + 13 + 0 * 40 lda SCREEN + 14 + 1 * 40 sta SCREEN + 14 + 24 * 40 lda SCREEN + 14 + 23 * 40 sta SCREEN + 14 + 0 * 40 lda SCREEN + 15 + 1 * 40 sta SCREEN + 15 + 24 * 40 lda SCREEN + 15 + 23 * 40 sta SCREEN + 15 + 0 * 40 lda SCREEN + 16 + 1 * 40 sta SCREEN + 16 + 24 * 40 lda SCREEN + 16 + 23 * 40 sta SCREEN + 16 + 0 * 40 lda SCREEN + 17 + 1 * 40 sta SCREEN + 17 + 24 * 40 lda SCREEN + 17 + 23 * 40 sta SCREEN + 17 + 0 * 40 lda SCREEN + 18 + 1 * 40 sta SCREEN + 18 + 24 * 40 lda SCREEN + 18 + 23 * 40 sta SCREEN + 18 + 0 * 40 lda SCREEN + 19 + 1 * 40 sta SCREEN + 19 + 24 * 40 lda SCREEN + 19 + 23 * 40 sta SCREEN + 19 + 0 * 40 lda SCREEN + 20 + 1 * 40 sta SCREEN + 20 + 24 * 40 lda SCREEN + 20 + 23 * 40 sta SCREEN + 20 + 0 * 40 lda SCREEN + 21 + 1 * 40 sta SCREEN + 21 + 24 * 40 lda SCREEN + 21 + 23 * 40 sta SCREEN + 21 + 0 * 40 lda SCREEN + 22 + 1 * 40 sta SCREEN + 22 + 24 * 40 lda SCREEN + 22 + 23 * 40 sta SCREEN + 22 + 0 * 40 lda SCREEN + 23 + 1 * 40 sta SCREEN + 23 + 24 * 40 lda SCREEN + 23 + 23 * 40 sta SCREEN + 23 + 0 * 40 lda SCREEN + 24 + 1 * 40 sta SCREEN + 24 + 24 * 40 lda SCREEN + 24 + 23 * 40 sta SCREEN + 24 + 0 * 40 lda SCREEN + 25 + 1 * 40 sta SCREEN + 25 + 24 * 40 lda SCREEN + 25 + 23 * 40 sta SCREEN + 25 + 0 * 40 lda SCREEN + 26 + 1 * 40 sta SCREEN + 26 + 24 * 40 lda SCREEN + 26 + 23 * 40 sta SCREEN + 26 + 0 * 40 lda SCREEN + 27 + 1 * 40 sta SCREEN + 27 + 24 * 40 lda SCREEN + 27 + 23 * 40 sta SCREEN + 27 + 0 * 40 lda SCREEN + 28 + 1 * 40 sta SCREEN + 28 + 24 * 40 lda SCREEN + 28 + 23 * 40 sta SCREEN + 28 + 0 * 40 lda SCREEN + 29 + 1 * 40 sta SCREEN + 29 + 24 * 40 lda SCREEN + 29 + 23 * 40 sta SCREEN + 29 + 0 * 40 lda SCREEN + 30 + 1 * 40 sta SCREEN + 30 + 24 * 40 lda SCREEN + 30 + 23 * 40 sta SCREEN + 30 + 0 * 40 lda SCREEN + 31 + 1 * 40 sta SCREEN + 31 + 24 * 40 lda SCREEN + 31 + 23 * 40 sta SCREEN + 31 + 0 * 40 lda SCREEN + 32 + 1 * 40 sta SCREEN + 32 + 24 * 40 lda SCREEN + 32 + 23 * 40 sta SCREEN + 32 + 0 * 40 lda SCREEN + 33 + 1 * 40 sta SCREEN + 33 + 24 * 40 lda SCREEN + 33 + 23 * 40 sta SCREEN + 33 + 0 * 40 lda SCREEN + 34 + 1 * 40 sta SCREEN + 34 + 24 * 40 lda SCREEN + 34 + 23 * 40 sta SCREEN + 34 + 0 * 40 lda SCREEN + 35 + 1 * 40 sta SCREEN + 35 + 24 * 40 lda SCREEN + 35 + 23 * 40 sta SCREEN + 35 + 0 * 40 lda SCREEN + 36 + 1 * 40 sta SCREEN + 36 + 24 * 40 lda SCREEN + 36 + 23 * 40 sta SCREEN + 36 + 0 * 40 lda SCREEN + 37 + 1 * 40 sta SCREEN + 37 + 24 * 40 lda SCREEN + 37 + 23 * 40 sta SCREEN + 37 + 0 * 40 lda SCREEN + 38 + 1 * 40 sta SCREEN + 38 + 24 * 40 lda SCREEN + 38 + 23 * 40 sta SCREEN + 38 + 0 * 40 ; vertical lda SCREEN + 1 + 1 * 40 sta SCREEN + 39 + 1 * 40 lda SCREEN + 38 + 1 * 40 sta SCREEN + 0 + 1 * 40 lda SCREEN + 1 + 2 * 40 sta SCREEN + 39 + 2 * 40 lda SCREEN + 38 + 2 * 40 sta SCREEN + 0 + 2 * 40 lda SCREEN + 1 + 3 * 40 sta SCREEN + 39 + 3 * 40 lda SCREEN + 38 + 3 * 40 sta SCREEN + 0 + 3 * 40 lda SCREEN + 1 + 4 * 40 sta SCREEN + 39 + 4 * 40 lda SCREEN + 38 + 4 * 40 sta SCREEN + 0 + 4 * 40 lda SCREEN + 1 + 5 * 40 sta SCREEN + 39 + 5 * 40 lda SCREEN + 38 + 5 * 40 sta SCREEN + 0 + 5 * 40 lda SCREEN + 1 + 6 * 40 sta SCREEN + 39 + 6 * 40 lda SCREEN + 38 + 6 * 40 sta SCREEN + 0 + 6 * 40 lda SCREEN + 1 + 7 * 40 sta SCREEN + 39 + 7 * 40 lda SCREEN + 38 + 7 * 40 sta SCREEN + 0 + 7 * 40 lda SCREEN + 1 + 8 * 40 sta SCREEN + 39 + 8 * 40 lda SCREEN + 38 + 8 * 40 sta SCREEN + 0 + 8 * 40 lda SCREEN + 1 + 9 * 40 sta SCREEN + 39 + 9 * 40 lda SCREEN + 38 + 9 * 40 sta SCREEN + 0 + 9 * 40 lda SCREEN + 1 + 10 * 40 sta SCREEN + 39 + 10 * 40 lda SCREEN + 38 + 10 * 40 sta SCREEN + 0 + 10 * 40 lda SCREEN + 1 + 11 * 40 sta SCREEN + 39 + 11 * 40 lda SCREEN + 38 + 11 * 40 sta SCREEN + 0 + 11 * 40 lda SCREEN + 1 + 12 * 40 sta SCREEN + 39 + 12 * 40 lda SCREEN + 38 + 12 * 40 sta SCREEN + 0 + 12 * 40 lda SCREEN + 1 + 13 * 40 sta SCREEN + 39 + 13 * 40 lda SCREEN + 38 + 13 * 40 sta SCREEN + 0 + 13 * 40 lda SCREEN + 1 + 14 * 40 sta SCREEN + 39 + 14 * 40 lda SCREEN + 38 + 14 * 40 sta SCREEN + 0 + 14 * 40 lda SCREEN + 1 + 15 * 40 sta SCREEN + 39 + 15 * 40 lda SCREEN + 38 + 15 * 40 sta SCREEN + 0 + 15 * 40 lda SCREEN + 1 + 16 * 40 sta SCREEN + 39 + 16 * 40 lda SCREEN + 38 + 16 * 40 sta SCREEN + 0 + 16 * 40 lda SCREEN + 1 + 17 * 40 sta SCREEN + 39 + 17 * 40 lda SCREEN + 38 + 17 * 40 sta SCREEN + 0 + 17 * 40 lda SCREEN + 1 + 18 * 40 sta SCREEN + 39 + 18 * 40 lda SCREEN + 38 + 18 * 40 sta SCREEN + 0 + 18 * 40 lda SCREEN + 1 + 19 * 40 sta SCREEN + 39 + 19 * 40 lda SCREEN + 38 + 19 * 40 sta SCREEN + 0 + 19 * 40 lda SCREEN + 1 + 20 * 40 sta SCREEN + 39 + 20 * 40 lda SCREEN + 38 + 20 * 40 sta SCREEN + 0 + 20 * 40 lda SCREEN + 1 + 21 * 40 sta SCREEN + 39 + 21 * 40 lda SCREEN + 38 + 21 * 40 sta SCREEN + 0 + 21 * 40 lda SCREEN + 1 + 22 * 40 sta SCREEN + 39 + 22 * 40 lda SCREEN + 38 + 22 * 40 sta SCREEN + 0 + 22 * 40 lda SCREEN + 1 + 23 * 40 sta SCREEN + 39 + 23 * 40 lda SCREEN + 38 + 23 * 40 sta SCREEN + 0 + 23 * 40 rts ; DRAGONS BE HERE SCREEN equ $0400 NEW_SCREEN equ $8000 COLOUR equ $d800 SCREEN_BORDER equ $d020 SCREEN_BG equ $d021 SCREEN_1A equ SCREEN + 0 + 0 * 40 SCREEN_1B equ SCREEN + 0 + 1 * 40 SCREEN_1C equ SCREEN + 0 + 2 * 40 SCREEN_2A equ SCREEN + 1 + 0 * 40 SCREEN_2B equ SCREEN + 1 + 1 * 40 SCREEN_2C equ SCREEN + 1 + 2 * 40 SCREEN_3A equ SCREEN + 2 + 0 * 40 SCREEN_3B equ SCREEN + 2 + 1 * 40 SCREEN_3C equ SCREEN + 2 + 2 * 40 SCREEN_C equ SCREEN_2B NEW_SCREEN_C equ NEW_SCREEN + 1 + 1 * 40 ; we could use zero page, but that doesn't improve performance one bit ; besides, dasm starts spamming with pointless "label mismatch" warnings... y_a: nop