; LIFE FOR APPLE-1
; UNKNOWN AUTHOR
; DISASSEMBLED BY ANTONINO PORCINO, 14TH SEP 2022

ECHO      = $FFEF    ; character echo routine in Woz monitor
KEY_DATA  = $D010    ; read key
KEY_CTRL  = $D011    ; control port

; zero page variables

PTR1      = $F0   ; $F0-$F1
PTR2      = $F2   ; $F2-$F3
PTR3      = $F6   ; $F6-$F7
PTR4      = $F8   ; $F8-$F9
CELLCOUNT = $F9   ; also PTR4 high byte
RND       = $FA   ; $FA-$FB random seed

;
; board geometry is 40x24 = 960 bytes
; there are two boards stored in memory:
; primary board:   $2440-$27FF
; secondary board: $2840-$2BFF
;
; the boards contains the actual characters that are printed on 
; the screen: live cell ("*"), dead cell (" ") and top/bottom border ("-")
;

BOARD    = $2440
BOARDEND = $2800

ROW0  = $2440
ROW1  = $2468
ROW22 = $27B0
ROW23 = $27D8

DEADCELL  = $A0          ; " " (space character)
ALIVECELL = $AA          ; "*"
FRAMECELL = $AD          ; "-"

   ORG $2000

;
; clears the primary board ($2400-$27FF) 
; bug: the area between $2400-$243F is cleared as well
;
          LDX #$00
          LDA #DEADCELL ; dead cell character
L2004     STA $2400,X   ; bug: this should be $2440
          STA $2500,X
          STA $2600,X
          STA $2700,X
          INX
          BNE L2004

;
; prints the welcome message (56 characters)
; (X is zero from previous BNE)
;

L2013     LDA MESSAGE,X
          JSR ECHO
          INX
          CPX #56      ; prints 56 characters of the message
          BNE L2013

;
; read and print from keyboard until ENTER is pressed
; typed characters are simply discarded, the purpose 
; is to increment the random seed in $FA-FB (RND variable)
;

L201E     JSR RDKEY
          CMP #$8D     ; enter key
          BNE L201E

;
; fills the board with a random number of live cells (from 32 to 95)
; bug: also writes out of the screen area between $2400 and $243F
;

          JSR GETRAND    ; gets a random number (0-255) in A
          AND #$3F       ; ranges it $00-$3F    
          ADC #$20       ; ranges it $20-$5F   (why carry is not cleared?)
          STA CELLCOUNT  ; number of cells to generate betwen $20-$5F (32-95)
 
          LDY #$00       ; keeps Y to 0 for "STA(),Y" instruction
L2030     JSR GETRAND    ; gets a random number in A
          AND #$03       ; ranges it 0-3
          CLC 
          ADC #BOARD/256 ; board high byte ($24)
          STA PTR1+1     ; high byte now ranges from $24-$27
          LDA RND        ; get random number directly from seed
          STA PTR1       ; PTR1 now points randomly to $2400-$27FF
          LDA #ALIVECELL ; alive cell character ("*")
          STA (PTR1),Y   ; set cell "ON" 
          DEC CELLCOUNT  ; decrement cell counter
          BNE L2030      ; loop until all cells are written

;
;$2046
; main game loop
;
MAINLOOP  LDA #BOARD/256  ;
          STA PTR4+1      ;
          LDA #BOARD%256  ;
          STA PTR4        ; sets PTR4 to $2440 

;
; marks top and bottom lines of the board to avoid out of bound checks
;

          LDX #39        ; counts 40 characters backward
          LDA #FRAMECELL ; board frame character ("-")
L2052     STA ROW23,X    ; write bottom line of the board
          STA ROW0 ,X    ; write top    line of the board
          DEX            ; decrement char counter  
          BPL L2052      ; loop if X >= 0

;
; prints the whole board from $2440 to $2800
; Y is already zero from above
;
L205B     LDA (PTR4),Y
          JSR ECHO
          INC PTR4
          BNE L205B
          INC PTR4+1
          LDA PTR4+1
          CMP #BOARDEND/256
          BNE L205B

;
; reads the keyboard between each screen printout
;
          JSR RDKEY

;
; copy primary board into secondary board (copies $2440-$27FF into $2840-$2BFF)
;
          LDY #$00
          JSR SETPTRS      ; set board pointers: PTR1=$2440 (primary), PTR2=$2840 (secondary)
L2074     LDA (PTR1),Y     ; read from primary
          STA (PTR2),Y     ; write into secondary
          INC PTR1
          INC PTR2
          BNE L2074
          INC PTR1+1
          INC PTR2+1
          LDA PTR1+1
          CMP #BOARDEND/256    ; check end of the board
          BNE L2074

;
; once the primary board is copied, it's used to count the number of 
; nearby cells around a live cell, so it's all set to 0
;
          JSR ZEROBOARD   

;
; scans the secondary board: for every live cell increment by one 
; the nearby cells in the primary board
;
; nearby cells are scanned using Y-indexing with the following Y values:
;
;   -41 -40 -39 
;    -1   *  +1           
;    39  40  41
;

L208B     LDY #$00
          LDA (PTR2),Y    ; read cell in the secondary board
          CMP #ALIVECELL  ; is cell alive?
          BNE L209D       ; if no go next cell
          LDY #39         ; yes it's alive, start Y indexing from 39
          JSR SUM6CELLS   ; sum cells with Y offsets: +/-39, +/-40, +/-41 (row above and below the live cell)
          LDY #1        
          JSR SUMYCELLS   ; sum cells with Y offsets: +/-1 (left/right of live cell)

          ; increment PTR1 and PTR2 to next cell
L209D     INC PTR1        
          INC PTR2
          BNE L208B
          INC PTR1+1
          INC PTR2+1
          LDA PTR1+1
          CMP #BOARDEND/256    ; check end of the board
          BNE L208B

;
; sums the score from bottom row into top, and top into bottom
; this "wraps" the playfield making it like a toroid (sort of)
;
; ROW1  = ROW1  + ROW23
; ROW22 = ROW22 + ROW0
;
          CLC
          LDX #39        ; count 40 characters (1 row)
L20B0     LDA ROW23,X
          ADC ROW1,X
          STA ROW1,X
          LDA ROW0,X
          ADC ROW22,X    ; (due to low score, carry it's always zero, no need to CLC)
          STA ROW22,X
          DEX
          BPL L20B0

;
; the actual GAME OF LIFE computation (Conway's rules)
; here secondary buffer contains the old board ("*" or " ")
; and primary buffer contains the number of nearby cells for each cell that is alive
;

          JSR SETPTRS       ; start from top of the boards 
L20C8     LDY #$00
          LDA (PTR1),Y
          CMP #2            ; two cells nearby?
          BNE L20D4         ; if not checks for three
          LDA (PTR2),Y      ; yes two cells around: it survives to next round
          BNE L20DE
L20D4     CMP #3            ; three cells nearby?
          BNE L20DC         ; if not then it's dead cell
          LDA #ALIVECELL    ; else it's alive (does not matter if it was alive or not)
          BNE L20DE         ; anything else is dead cell
L20DC     LDA #DEADCELL     ; set next cell dead
L20DE     STA (PTR1),Y      ; write the cell on the board

          INC PTR1
          INC PTR2
          BNE L20C8
          INC PTR1+1
          INC PTR2+1
          LDA PTR2+1
          CMP #$2C          ; check end of the secondary board (ends at $2C00)
          BNE L20C8

          JMP MAINLOOP

; set pointers to:
; PTR1 = $2440
; PTR2 = $2840
; $20F3
SETPTRS   LDA #$24
          STA PTR1+1
          LDA #$28
          STA PTR2+1
          LDA #$40
          STA PTR1
          STA PTR2
          RTS

; $2102
; reads a key and displays it on the terminal
; returns key in A
; increments a time counter $FA-$FB
;         
RDKEY     INC RND
          BNE L2108
          INC RND+1
L2108     LDA KEY_CTRL
          BPL RDKEY
          LDA KEY_DATA
          JSR ECHO
          RTS

; $2114
SUM6CELLS JSR SUMYCELLS
          INY
          CPY #$2A
          BNE SUM6CELLS
          RTS
;$211D
SUMYCELLS ; increment cell at (PTR1),Y
          CLC             
          LDA (PTR1),Y    
          ADC #$01        
          STA (PTR1),Y    

          STY CELLCOUNT   ; save Y into CELLCOUNT  

          ; PTR3 = PTR1 - Y
          SEC
          LDA PTR1
          SBC CELLCOUNT
          STA PTR3
          LDA PTR1+1
          SBC #$00
          STA PTR3+1      

          ; increment cell at (PTR3),Y e.g. (PTR1),-Y
          LDY #$00
          LDA (PTR3),Y
          CLC
          ADC #$01
          STA (PTR3),Y
          LDY CELLCOUNT  ; restores Y
          RTS
;          
;$213F
; (possibly) advances the random number generator 
; and loads a random number into A
; uses PTR3 as temporary variable?
GETRAND   LDA RND+1
          STA PTR3
          LDA RND
          ASL A
          ROL PTR3
          ASL A
          ROL PTR3
          CLC
          ADC RND
          PHA
          LDA PTR3
          ADC RND+1
          STA RND+1
          PLA
          ADC #$11
          STA RND
          LDA RND+1
          ADC #$36
          STA RND+1
          RTS

;$2161
;fills the primary board ($2440-$27FF) all with $00
ZEROBOARD JSR SETPTRS
          LDA #$00
          TAY
L2167     STA (PTR1),Y
          INC PTR1
          BNE L2167
          INC PTR1+1
          LDX PTR1+1
          CPX #BOARDEND/256  ; check end of the board
          BNE L2167
          JSR SETPTRS        ; reset correct pointers on exit
          RTS

L2179:
   DB A0 ; unused byte

; $2180
MESSAGE:
   DB $A0,$A0,$A0,$A0,$A0,$A0,$8D,$8D
   DB $8D,$8D,$C3,$CF,$CE,$D7,$C1,$D9
   DB $A7,$D3,$A0,$C7,$C1,$CD,$C5,$A0
   DB $CF,$C6,$A0,$CC,$C9,$C6,$C5,$8D
   DB $8D,$D0,$CC,$C5,$C1,$D3,$C5,$A0
   DB $D4,$D9,$D0,$C5,$A0,$D9,$CF,$D5
   DB $D2,$A0,$C6,$D5,$CC,$CC,$A0,$CE
   DB $C1,$CD,$C5,$AE,$8D,$8D,$00             

// $21B9
                            
