8bitworkshop/presets/apple2/conway.a

556 lines
20 KiB
Plaintext

; Conway II
; Lee W. Fastenau
; thelbane@gmail.com
; Created 03/14/2017
; from https://github.com/thelbane/ConwayII
processor 6502
ZPA0 equ $06
ZPA1 equ $07
ZPA2 equ $08
ZPA3 equ $09
HTAB equ $24
VTAB equ $25
TXTROW equ $28
ZPB0 equ $EB
ZPB1 equ $EC
ZPB2 equ $ED
ZPB3 equ $EE
ZPB4 equ $EF
ZPC0 equ $FA
ZPC1 equ $FB
ZPC2 equ $FC
ZPC3 equ $FD
STACK equ $100
TXTPG0 equ $0400
TXTPG1 equ $0800
CLICK equ $C030
HOME equ $FC58
RDKEY EQU $FD0C
COUT equ $FDED
OUTPORT equ $FE95
EXITDOS equ $03D0
mac LOG_REGION
if {3} && >. != >{2}
echo "(",{2},"-",.,")",{1},"*** BOUNDARY CROSSED ***"
err
else
echo "(",{2},"-",.,")",{1}
endif
endm
; ------------------------------------
; Build Options
; ------------------------------------
NOISY equ 0 ; 0 = Sound off, 1 = Sound on
CHARSET equ 1 ; 0 = Olde Skoole, 1 = Pixel, 2 = Inverse, 3 = Small O's, 4 = Enhanced
INIT_PATTERN equ 0 ; 0 = Glider gun, 1 = "Random", 2 = Edge test
TEST_PERF equ 0 ; 0 = Normal, 1 = Instrument for emulator cycle counting (forces Glider gun layout and sound off)
; ------------------------------------
; Constants
; ------------------------------------
soundEnabled equ NOISY && !TEST_PERF
initialPattern equ INIT_PATTERN - [TEST_PERF * INIT_PATTERN]
textRow equ ZPA0
textRowH equ ZPA1
mainData equ ZPC0
mainDataH equ ZPC1
altData equ ZPC2
altDataH equ ZPC3
currentPage equ ZPA2
temp equ ZPA3
fieldWidth equ 40
fieldHeight equ 24
dataWidth equ fieldWidth+2
dataHeight equ fieldHeight+2
normalText equ %10000000 ; 'X | normalText
inverseText equ %00111111 ; 'X & inverseText
if CHARSET == 0
charOn equ '* | normalText
charOff equ '. | normalText
endif
if CHARSET == 1
charOn equ ' & inverseText
charOff equ ' | normalText
endif
if CHARSET == 2
charOn equ ' | normalText
charOff equ ': & inverseText
endif
if CHARSET == 3
charOn equ 'o | normalText
charOff equ ' | normalText
endif
if CHARSET == 4
charOn equ $ff ; | normalText
charOff equ ' | normalText
endif
n_offset equ dataWidth+1 ; Alt data topleft offset from current cell
y_topleft equ 0 ; Alt data pointer offsets
y_top equ 1
y_topright equ 2
y_left equ dataWidth
y_right equ dataWidth+2
y_bottomleft equ dataWidth*2
y_bottom equ dataWidth*2+1
y_bottomright equ dataWidth*2+2
; ------------------------------------
; Entry Point
; ------------------------------------
seg program
org $803
start subroutine
lda #0
sta currentPage ; Point main data segment to first block
jsr OUTPORT ; PR#0 (Set output to 40-column text screen)
jsr initScreen ; Render initial cell layout
jsr updateData ; Initialize backing data based on displayed cells
if TEST_PERF
jsr perfTest
else
jsr runLoop
endif
jmp EXITDOS
runLoop subroutine
.loop jsr iterate ; Modify and display next generation
jmp .loop ; Until cows come home
perfTest subroutine
jsr RDKEY
.startTimer
lda #50
sta .counter
.loop jsr iterate
dec .counter
bne .loop
.endTimer
.break jsr RDKEY
echo "Breakpoint:", .break
rts
.counter ds.b 1
echo "START TIMER BREAKPOINT:",.startTimer
echo "END TIMER BREAKPOINT:",.endTimer
mac INCREMENT_ADC
ldy #y_{1} ; +2 2
lda (altData),y ; +5/6 8
adc #1 ; +5 13 Relies on carry being clear
sta (altData),y ; +6 19
endm
mac INCREMENT_INX
ldy #y_{1} ; +2 2
lda (altData),y ; +5/6 8
tax ; +2 10
inx ; +2 12
txa ; +2 14
sta (altData),y ; +6 20
endm
mac INCREMENT
INCREMENT_ADC {1}
endm
iterate subroutine
jsr toggleDataPages
jsr clearBorders
lda #fieldHeight-1
sta .row
.rowLoop jsr getTextRow
lda #fieldWidth-1
sta .column
lda #0
ldy #y_top ; clean up stale data
sta (altData),y
ldy #y_topright
sta (altData),y
.columnLoop ldy .column ; get neighbor bit flags
lda (mainData),y ; at current data address
tay
lda rulesTable,y ; convert bit flags to cell state character (or 0 for do nothing)
beq .doNothing ; rule says do nothing, so update the neighbor data
ldy #0 ; .column
.column equ .-1
sta (textRow),y ; set char based on rule
bne .setBits
.doNothing ldy .column
lda (textRow),y
.setBits cmp #charOn ; A = cell character
bne .clearTopLeft ; cell is disabled, so clear the topleft neighbor
if soundEnabled
bit CLICK
endif
ldy #y_topleft ; set top left value to one (previous value is stale)
lda #1
sta (altData),y
if soundEnabled
bit CLICK ; (Pretend I'm not here... I just click the speaker)
endif
clc
INCREMENT top
INCREMENT topright
INCREMENT left
INCREMENT right
INCREMENT bottomleft
INCREMENT bottom
INCREMENT bottomright
jmp .continue
.clearTopLeft ldy #y_topleft ; cell is off, so clear top left value to remove stale data
lda #0
sta (altData),y
.continue sec
lda altData
sbc #1
sta altData
lda altDataH
sbc #0
sta altDataH
.nextColumn dec .column
bmi .nextRow
jmp .columnLoop
.nextRow sec
lda mainData
sbc #dataWidth
sta mainData
lda mainDataH
sbc #0
sta mainDataH
sec
lda altData
sbc #2
sta altData
lda altDataH
sbc #0
sta altDataH
dec .row
lda #0 ; .row
.row equ .-1
bmi .end
jmp .rowLoop
.end rts
updateData subroutine
jsr toggleDataPages
jsr clearBorders
lda #fieldHeight-1
sta .row
.rowLoop jsr getTextRow
lda #fieldWidth-1
sta .column
lda #0
ldy #y_top ; clean up stale data
sta (altData),y
ldy #y_topright
sta (altData),y
.columnLoop ldy #0 ; .column
.column equ .-1
lda (textRow),y
cmp #charOff
beq .clearTopLeft
ldy #y_topleft ; set top left value to one (previous value is stale)
lda #1
sta (altData),y
clc
INCREMENT top
INCREMENT topright
INCREMENT left
INCREMENT right
INCREMENT bottomleft
INCREMENT bottom
INCREMENT bottomright
jmp .nextColumn
.clearTopLeft ldy #y_topleft
lda #0
sta (altData),y
.nextColumn sec
lda altData
sbc #1
sta altData
lda altDataH
sbc #0
sta altDataH
dec .column
bpl .columnLoop
.nextRow sec
lda altData
sbc #2
sta altData
lda altDataH
sbc #0
sta altDataH
dec .row
lda #0 ; .row
.row equ .-1
bmi .end
jmp .rowLoop
.end rts
toggleDataPages subroutine ; toggles the current data page and sets up the pointers
lda #1
eor currentPage
sta currentPage
bne .page1
.page0 lda <#datapg0_lastRow
sta mainData
lda >#datapg0_lastRow
sta mainDataH
lda <#datapg1_tln
sta altData
lda >#datapg1_tln
sta altDataH
jmp .continue
.page1 lda <#datapg1_lastRow
sta mainData
lda >#datapg1_lastRow
sta mainDataH
lda <#datapg0_tln
sta altData
lda >#datapg0_tln
sta altDataH
.continue rts
clearBorders subroutine
mac CLEAR_BORDERS
.bottomRow set datapg{2}_end - [dataWidth * 2] + 1
ldx #fieldWidth
.hloop lda #0
sta .bottomRow,x
dex
bne .hloop
.rightColumn set ZPB0
.rightAddr set datapg{1}_end - dataWidth - 2
lda <#.rightAddr
sta <.rightColumn
lda >#.rightAddr
sta >.rightColumn
ldy #0
ldx #fieldHeight
.vloop lda #0
sta (.rightColumn),y
lda #dataWidth
sec
sbc <.rightColumn
sta <.rightColumn
lda #0
sbc >.rightColumn
sta >.rightColumn
dex
bne .vloop
endm
lda currentPage
bne .page1
.page0 CLEAR_BORDERS 0,1
rts
.page1 CLEAR_BORDERS 1,0
rts
initScreen subroutine
lda <#initData
sta mainData
lda >#initData
sta mainDataH
lda #initDataLen-1 ; get data length
sta .dataoffset ; save it
lda #fieldHeight-1 ; load the field height
sta .row ; save in row counter
.1 jsr getTextRow ; update textRow (A = row)
lda #fieldWidth-1 ; load the field width (reset every new row)
sta .column ; save in column counter
ldy .dataoffset
lda (mainData),y ; get the current data byte
sta .byte ; save it
lda #8 ; init the byte counter
sta .bit ; save in bit counter
.2 ldy .column
lda #0
.byte equ .-1
lsr
sta .byte
bcs .turnOn
.turnOff lda #charOff
bne .draw
.turnOn lda #charOn
.draw sta (textRow),y
dec .bit
bne .skipbit
lda #8 ; reset bit counter
sta .bit ; decrease data byte reference
sec
dec .dataoffset
ldy #0 ; .dataoffset
.dataoffset equ .-1
lda (mainData),y
sta .byte
.skipbit lda .column ; start to calculate init byte offset
dec .column
ldy #0 ; .column
.column equ .-1
bpl .2
dec .row
lda #0 ; .row
.row equ .-1
bpl .1
rts
.bit ds.b 1
; inputs:
; A = row
; outputs:
; A = ?, X = A << 1, textRow = address of first character in row A
getTextRow subroutine
asl
tax
lda textRowsTable,x
sta textRow
lda textRowsTable+1,x
sta textRowH
rts
rulesTable dc.b charOff ;0 neighbors
dc.b charOff ;1
dc.b 0 ;2
dc.b charOn ;3
dc.b charOff ;4
dc.b charOff ;5
dc.b charOff ;6
dc.b charOff ;7
dc.b charOff ;8
; ------------------------------------
; Tables
; ------------------------------------
textRowsTable subroutine ; Lookup table for text page 0 row addresses
.pg equ 1024
.y set 0
repeat 24
dc.w .pg + (.y & %11111000) * 5 + ((.y & %00000111) << 7)
.y set .y + 1
repend
LOG_REGION "textRowsTable", textRowsTable, 0
if initialPattern == 0 ; Glider gun
initData dc.b %00000000,%00000000,%00000000,%00000000,%00000000
dc.b %00000000,%00000000,%00000000,%01000000,%00000000
dc.b %00000000,%00000000,%00000001,%01000000,%00000000
dc.b %00000000,%00000110,%00000110,%00000000,%00011000
dc.b %00000000,%00001000,%10000110,%00000000,%00011000
dc.b %01100000,%00010000,%01000110,%00000000,%00000000
dc.b %01100000,%00010001,%01100001,%01000000,%00000000
dc.b %00000000,%00010000,%01000000,%01000000,%00000000
dc.b %00000000,%00001000,%10000000,%00000000,%00000000
dc.b %00000000,%00000110,%00000000,%00000000,%00000000
dc.b %00000000,%00000000,%00000000,%00000000,%00000000
dc.b %00000000,%00000000,%00000000,%00000000,%00000000
dc.b %00000000,%00000000,%00000000,%00000000,%00000000
dc.b %00000000,%00000000,%00000000,%00000000,%00000000
dc.b %00000000,%00000000,%00000000,%00000000,%00000000
dc.b %00000000,%00000000,%00000000,%00000000,%00000000
dc.b %00000000,%00000000,%00000000,%00000000,%00000000
dc.b %00000000,%00000000,%00000000,%00000000,%00000000
dc.b %00000000,%00000000,%00000000,%00000000,%00000000
dc.b %00000000,%00000000,%00000000,%00000000,%00000000
dc.b %00000000,%00000000,%00000000,%00000000,%00000000
dc.b %00000000,%00000000,%00000000,%00000000,%00000000
dc.b %00000000,%00000000,%00000000,%00000000,%00000000
dc.b %00000000,%00000000,%00000000,%00000000,%00000000
endif
if initialPattern == 1 ; "Random"
initData dc.b %00000000,%00000000,%00000000,%00000000,%00000000
dc.b %00000000,%00000000,%00000000,%00000000,%00000000
dc.b %00000000,%00000000,%00000000,%00000000,%01000000
dc.b %00000000,%00000000,%00000000,%00000000,%10100000
dc.b %00000000,%00000000,%00000000,%00000000,%10100000
dc.b %00000000,%00000000,%00000000,%00000000,%01000000
dc.b %00000000,%00000000,%00000000,%00000000,%00000000
dc.b %00000000,%00000000,%00000000,%00000000,%00000000
dc.b %00000000,%00000000,%00000000,%00000000,%00000000
dc.b %00000000,%00000000,%00000000,%00000000,%00000000
dc.b %00000000,%00000000,%00000000,%00000000,%00000000
dc.b %00000000,%00000000,%00000000,%00000000,%00000000
dc.b %00000000,%00000000,%00000000,%00000000,%00000000
dc.b %00000000,%00000000,%00000000,%00000000,%00000000
dc.b %00000000,%00000000,%00000000,%00000000,%00000000
dc.b %00000000,%00000000,%00000000,%00000000,%00000000
dc.b %00000000,%00100000,%00000000,%00000000,%00000000
dc.b %00000000,%10110000,%00000000,%00000000,%00000000
dc.b %00000000,%10100000,%00000000,%00000000,%00000000
dc.b %00000000,%10000000,%00000000,%00000000,%00000000
dc.b %00000010,%00000000,%00000000,%00000000,%00000000
dc.b %00001010,%00000000,%00000000,%00000000,%00000000
dc.b %00000000,%00000000,%00000000,%00000000,%00000000
dc.b %00000000,%00000000,%00000000,%00000000,%00000000
endif
if initialPattern == 2 ; Edge test
initData dc.b %11000000,%00000000,%00011000,%00000000,%00000011
dc.b %11000000,%00000000,%00100100,%00000000,%00000011
dc.b %00000000,%00000000,%00011000,%00000000,%00000000
dc.b %00000000,%00000000,%00000000,%00000000,%00000000
dc.b %00000000,%00000000,%00000000,%00000000,%00000000
dc.b %11100000,%00000000,%00000000,%00000000,%00000111
dc.b %00000000,%00000000,%00000000,%00000000,%00000000
dc.b %00000000,%00000000,%00000000,%00000000,%00000000
dc.b %00000000,%00011100,%00000000,%00111000,%00000000
dc.b %00000000,%00010000,%11011011,%00001000,%00000000
dc.b %00000000,%00001000,%11011011,%00010000,%00000000
dc.b %00000000,%00000000,%00000000,%00000000,%00000000
dc.b %00000000,%00000000,%00000000,%00000000,%00000000
dc.b %00000000,%00001000,%11011011,%00010000,%00000000
dc.b %00000000,%00010000,%11011011,%00001000,%00000000
dc.b %00000000,%00011100,%00000000,%00111000,%00000000
dc.b %00000000,%00000000,%00000000,%00000000,%00000000
dc.b %00000000,%00000000,%00000000,%00000000,%00000000
dc.b %11100000,%00000000,%00000000,%00000000,%00000111
dc.b %00000000,%00000000,%00000000,%00000000,%00000000
dc.b %00000000,%00000000,%00000000,%00000000,%00000000
dc.b %00000000,%00000000,%00011000,%00000000,%00000000
dc.b %11000000,%00000000,%00100100,%00000000,%00000011
dc.b %11000000,%00000000,%00011000,%00000000,%00000011
endif
initDataLen equ .-initData
dataSeg equ .
seg.u conwayData ; uninitialized data segment
org dataSeg
datapg0 ds.b dataWidth * dataHeight ; data page 0
datapg0_lastRow equ . - dataWidth - fieldWidth ; first visible cell of the last row
datapg0_tln equ . - [n_offset * 2] ; topleft neighbor of the bottomright-most visible cell
datapg0_end equ .
datapg1 ds.b dataWidth * dataHeight ; data page 1
datapg1_lastRow equ . - dataWidth - fieldWidth ; first visible cell of the last row
datapg1_tln equ . - [n_offset * 2] ; topleft neighbor of the bottomright-most visible cell
datapg1_end equ .