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
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
|
;;;
;; Implements the pool of bullets available when pressing B. The main code
;; should just want to call `init` for initializing the memory space, and then
;; `update` on each main game loop iteration. The following addresses are
;; reserved:
;; -> $40-$41: internal data.
;; -> $0208-$0213: OAM data (this is a pool of three sprites).
;;
;; NOTE: the code in here touch the addresses on OAM directly, instead of
;; keeping them in some internal addresses on zero page. This is usually not a
;; good idea, and more so if we were to do collision checking and stuff like
;; that. That being said, because of its simplicity, it's not that big of a
;; deal.
.scope Bullets
;; The number of bullets shown on screen for the current frame.
zp_bullets_screen = $40
;; Frame counter. See `FRAMES` below.
zp_frames = $41
;; How many frames have to pass to allow the user to shoot another bullet
;; after the previous one.
FRAMES = 15
;; Initializes the pool of bullets.
.proc init
;; Initializing variables.
lda #0
sta zp_bullets_screen
lda #FRAMES
sta zp_frames
;; Set X and Y positions off-screen for the three available slots.
lda #$FF
sta $208
sta $20C
sta $210
sta $20B
sta $20F
sta $213
;; All three sprites are instances of the same tile.
lda #4
sta $209
sta $20D
sta $210
;; Nothing special for these sprites, set attributes to 0.
lda #0
sta $20A
sta $20E
sta $212
rts
.endproc
;; Update bullets on screen, check if a new one has to be shown, etc.
.proc update
;; If the frame counter has the same value as our allowed one, we can go
;; into the `bullets_pressed` subroutine, otherwise we will skip it
;; altogether.
lda zp_frames
cmp #FRAMES
beq @check
inc zp_frames
jmp @position
@check:
jsr bullets_pressed
@position:
jsr update_positions
rts
.endproc
;; Shows a new bullet right in front of the ship if the player requested it
;; and it is possible.
.proc bullets_pressed
;; If we reached the maximum of bullets on screen, return early.
lda zp_bullets_screen
cmp #3
bne :+
rts
:
;; If the B button is not pressed, return early.
lda #Joypad::BUTTON_B
and Joypad::m_buttons1
bne :+
rts
:
;; The following code will loop through the three bullet sprites and
;; find one that is free (off-screen). If one could be found, it will
;; assign it to the ship's position.
ldx #0
@loop:
;; Is the Y position on this sprite $FF? If so then it's free and we can
;; jump into @set so we re-initialize the position for this bullet.
lda $208, x
cmp #$FF
beq @set
;; Oops, that was not the case. At this point, is this the last bullet?
;; If so, we can go to the @end, otherwise we increase the index and try
;; again.
cpx #8
beq @end
inx
inx
inx
inx
jmp @loop
@set:
;; At the current index we have a bullet to initialize. Hence, give it
;; the Y value from the player and the X one (+4 so it's at the center
;; of the ship on the X axis).
lda Player::zp_screen_y
sta $208, x
lda Player::zp_screen_x
clc
adc #4
inx
inx
inx
sta $208, x
;; Reset the `zp_frames` so to disallow too many bullets being shot at
;; once, and increate the `zp_bullets_screen` variable.
lda #0
sta zp_frames
inc zp_bullets_screen
@end:
rts
.endproc
;; Updates the positions from the bullets being shown on screen. If one
;; bullet goes off screen, then it's properly freed so it can be picked up
;; by another B press.
.proc update_positions
ldx #0
@loop:
;; Do nothing if the bullet is free.
lda $208, x
cmp #$FF
beq @next
;; The indexed bullet is shown on screen, and we should make it move
;; upward by 10 units. That being said, if its Y position is lesser or
;; equal than 10, we can directly free it now. Otherwise, just update
;; the Y value by performing this subtraction.
cmp #10
bcc @free
beq @free
sec
sbc #10
jmp @save
@free:
;; This bullet should be freed, decrease `zp_bullets_screen` and set `a`
;; to an off-screen value.
dec zp_bullets_screen
lda #$FF
@save:
;; Either way you reach this, in `a` we have the Y value to be stored.
sta $208, x
@next:
;; If we are already at the last bullet, jump to the @end. Otherwise
;; increase the index and loop again.
cpx #8
beq @end
inx
inx
inx
inx
jmp @loop
@end:
rts
.endproc
.endscope
|