aboutsummaryrefslogtreecommitdiff
path: root/drunkwalk.s
blob: 8cea4cf4cbb4fc6ca13d82c26cb1510d61539c5f (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
;;; Subroutine: drunkwalk
;;; Spawn a particle, walk it randomly until it's adjacent to a set
;;; pixel, then draw it. If it goes out of bounds, start over (spawn
;;; another).
;;; This is the innermost loop, so it should be as optimized as
;;; possible.

; Here be self-modifying code. All the 'cpx #0' and 'cpy #0' get
; their operands modified by set_limits.

; BEWARE!
; Y and X are backwards: Y holds the X coordinate, and X holds the
; Y coordinate. Has to be, because there's no (zpind),x addressing
; mode.

; TODO: Fix the cycle counts. I did them from my years-old memory, and
; I got at least one wrong, after looking it up again.

drunkwalk:
oob:
 ldy RANDOM ; spawn a new particle
 lda (spawn_x),y
 sta cursor_x
 lda (spawn_y),y
 sta cursor_y
 tax
 ldy cursor_x

 lda lineaddrs_l,x    ; 5
 sta pixptr           ; 3
 lda lineaddrs_h,x    ; 5
 sta pixptr+1         ; 3

move_pixel:
 bit RANDOM     ; 4 ; use top 2 bits (probably more random, definitely faster)
 bmi lr         ; 3/4
 bvc down       ; 3/4
up:
 dex            ; 2 ; N=1 V=1 up
selfmod_ymin = * + 1
 cpx #0         ; 2
 beq oob        ; 3
 stx cursor_y
 jmp check_lru
down:
 inx            ; 2 ; N=1 V=0 down
selfmod_ymax = * + 1
 cpx #0         ; 2
 beq oob        ; 3
 stx cursor_y
 jmp check_lrd
lr:
 bvc right      ; 3/4
left:
 dey            ; 3 ; N=0 V=1 left
selfmod_xmin = * + 1
 cpy #0         ; 2
 beq oob        ; 3
 sty cursor_x
 ; check left neighbor (we just vacated the right one)
 dey
 lda (pixptr),y
 bne stick
 iny
 jmp check_ud    ; 3 ; still have to check Y (up/down) neighbors.
right:
 iny            ; 3 ; N=0 V=0 right
selfmod_xmax = * + 1
 cpy #0         ; 2
 beq oob        ; 3
 sty cursor_x
 ; check right neighbor (we just vacated the left one)
 iny
 lda (pixptr),y
 bne stick
 dey
 ; fall through to check_ud

check_ud:
 ; check up neighbor
 lda lineaddrs_l-1,x
 sta pixptr2
 lda lineaddrs_h-1,x
 sta pixptr2+1
 lda (pixptr2),y
 bne stick

 ; check down neighbor
 lda lineaddrs_l+1,x
 sta pixptr2
 lda lineaddrs_h+1,x
 sta pixptr2+1
 lda (pixptr2),y
 bne stick
 jmp move_pixel ; pixel didn't stick, move it again.

check_lru:
 ; pixel's Y coord changed, must update pointer.
 lda lineaddrs_l,x    ; 5
 sta pixptr           ; 3
 lda lineaddrs_h,x    ; 5
 sta pixptr+1         ; 3

 ; check left neighbor
 dey
 lda (pixptr),y
 bne stick
 iny
 ; check right neighbor
 iny
 lda (pixptr),y
 bne stick
 dey
 ; check up neighbor
 lda lineaddrs_l-1,x
 sta pixptr2
 lda lineaddrs_h-1,x
 sta pixptr2+1
 lda (pixptr2),y
 bne stick
 jmp move_pixel ; pixel didn't stick, move it again.

check_lrd:
 ; pixel's Y coord changed, must update pointer.
 lda lineaddrs_l,x    ; 5
 sta pixptr           ; 3
 lda lineaddrs_h,x    ; 5
 sta pixptr+1         ; 3

 ; check left neighbor
 dey
 lda (pixptr),y
 bne stick
 iny
 ; check right neighbor
 iny
 lda (pixptr),y
 bne stick
 dey
 ; check down neighbor
 lda lineaddrs_l+1,x
 sta pixptr2
 lda lineaddrs_h+1,x
 sta pixptr2+1
 lda (pixptr2),y
 bne stick
 jmp move_pixel ; pixel didn't stick, move it again.

stick:
 ; pixel stuck next to an existing pixel, draw it and return.
 ldx cursor_y
 lda lineaddrs_l,x
 sta pixptr
 lda lineaddrs_h,x
 sta pixptr+1
 ldy cursor_x
 lda #1
 sta (pixptr),y
 rts