aboutsummaryrefslogtreecommitdiff
path: root/src/over.s
blob: e5855e39ac89861b45511325a035869651211e2b (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
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
.segment "CODE"

.scope Over
    ;; Has the "Game over" screen been displayed yet?
    zp_displayed = $10

    ;; Timer set whenever the "Game over" screen has been displayed. Whenever it
    ;; times out, then the player is redirected to the title screen.
    zp_timer = $11

    ;; Amount of time the player has to wait for the title screen to appear
    ;; again.
    TIMER_VALUE = HZ * 3

    ;; Initialize all variables for the "Game over" screens.
    .proc init
        lda #0
        sta Over::zp_displayed
        sta Over::zp_timer

        rts
    .endproc

    ;; Handle a "Game over" screen. It has two phases:
    ;;   1. Render a "Game over" message.
    ;;   2. Wait for a timer to time out.
    ;; It will set 1 to the 'a' register if the timer has run out, signaling
    ;; that the game can start over. Otherwise it sets 0 to the 'a' register.
    .proc handle
        lda #0
        sta Globals::zp_arg3

        ;; Has the "Game over" screen been displayed? If not do it now.
        lda Over::zp_displayed
        bne @do_handle
        jsr Over::render
        jmp @end

    @do_handle:
        lda Over::zp_timer
        bne @dec_timer
        inc Globals::zp_arg3
        beq @end
    @dec_timer:
        dec Over::zp_timer

    @end:
        lda Globals::zp_arg3
        rts
    .endproc

    ;; Render the "Game over" message to the screen. This is done in two
    ;; phases. We first ensure to disable the PPU, and in the second phase we do
    ;; the actual writing.
    .proc render
        ;; Is PPU disabled? If it is then jump into rendering the screen
        ;; directly.
        lda PPU::zp_mask
        beq @do_render

        ;; Nope! Force the PPU to be disabled and quit.
        lda Globals::zp_flags
        ora #%01000000
        sta Globals::zp_flags
        lda #$00
        sta PPU::zp_mask
        rts

    @do_render:
        jsr Over::clear_out_screen
        lda Globals::zp_flags
        and #$03
        cmp #2
        beq @regular
        jsr Over::render_coin_game_over
        jmp @next
    @regular:
        jsr Over::render_regular_game_over

    @next:
        ;; Enable back the PPU (only background).
        lda #%00001110
        sta PPU::zp_mask

        ;; Update PPU registers.
        lda #%01000000
        ora Globals::zp_flags
        sta Globals::zp_flags

        ;; Set the "Game over" message as displayed and fire up the timer.
        lda #1
        sta Over::zp_displayed
        lda #Over::TIMER_VALUE
        sta Over::zp_timer

        rts
    .endproc

    ;; Remove all platforms and the ground.
    .proc clear_out_screen
        ;; Remove left platform.
        bit PPU::m_status
        ldx #$29
        stx PPU::m_address
        ldx #$83
        stx PPU::m_address
        lda #$00
        sta PPU::m_data
        sta PPU::m_data
        sta PPU::m_data
        sta PPU::m_data
        sta PPU::m_data
        sta PPU::m_data
        sta PPU::m_data

        ;; Remove center platform.
        bit PPU::m_status
        ldx #$29
        stx PPU::m_address
        ldx #$EF
        stx PPU::m_address
        sta PPU::m_data
        sta PPU::m_data
        sta PPU::m_data
        sta PPU::m_data
        sta PPU::m_data

        ;; Remove right platform.
        bit PPU::m_status
        ldx #$29
        stx PPU::m_address
        ldx #$38
        stx PPU::m_address
        sta PPU::m_data
        sta PPU::m_data
        sta PPU::m_data
        sta PPU::m_data
        sta PPU::m_data
        sta PPU::m_data
        sta PPU::m_data

        ;; Ground
        bit PPU::m_status
        ldx #$2B
        stx PPU::m_address
        ldx #$20
        stx PPU::m_address

        ldx #$20
    @clear_ground_loop:
        sta PPU::m_data
        dex
        bne @clear_ground_loop

        ;; The low part of the rocket.
        bit PPU::m_status
        ldx #$2B
        stx PPU::m_address
        ldx #$15
        stx PPU::m_address
        sta PPU::m_data
        sta PPU::m_data

        ;; High part of the rocket.
        bit PPU::m_status
        ldy #$2A
        sty PPU::m_address
        ldx #$75
        stx PPU::m_address
        sta PPU::m_data
        sta PPU::m_data

        bit PPU::m_status
        sty PPU::m_address
        ldx #$95
        stx PPU::m_address
        sta PPU::m_data
        sta PPU::m_data

        bit PPU::m_status
        sty PPU::m_address
        ldx #$B5
        stx PPU::m_address
        sta PPU::m_data
        sta PPU::m_data

        ;; Middle part of the rocket.
        bit PPU::m_status
        sty PPU::m_address
        ldx #$D5
        stx PPU::m_address
        sta PPU::m_data
        sta PPU::m_data

        bit PPU::m_status
        sty PPU::m_address
        ldx #$F5
        stx PPU::m_address
        sta PPU::m_data
        sta PPU::m_data

        rts
    .endproc

    ;; Render the regular "Game over" screen.
    .proc render_regular_game_over
        ;; Set the position.
        bit PPU::m_status
        ldx #$29
        stx PPU::m_address
        ldx #$6C
        stx PPU::m_address

        ;; And just iterate over the "message" until we reach the end of string
        ;; $FF character.
        ldx #0
    @message_loop:
        lda message, x
        cmp #$FF
        beq @out
        sta PPU::m_data
        inx
        bne @message_loop

    @out:
        ;; Reset attributes for the end of the message.
        bit PPU::m_status
        ldx #$2B
        stx PPU::m_address
        ldx #$D5
        stx PPU::m_address
        lda #0
        sta PPU::m_data
        sta PPU::m_data
        sta PPU::m_data

        rts

    message:
        ;; "GAME "
        .byte $21, $1B, $27, $1F, $00
        ;; "OVER"
        .byte $29, $30, $1F, $2C, $FF
    .endproc

    ;; Render the "Game over" in the case the player has collected SUSE's
    ;; coin. There's clearly a way to merge this function with
    ;; Over::render_regular_game_over(), but we have so much ROM space left that
    ;; I don't even care.
    .proc render_coin_game_over
        ;; Set the position.
        bit PPU::m_status
        ldx #$29
        stx PPU::m_address
        ldx #$64
        stx PPU::m_address

        ;; And just iterate over the "message" until we reach the end of string
        ;; $FF character.
        ldx #0
    @message_loop:
        lda message, x
        cmp #$FF
        beq @out
        sta PPU::m_data
        inx
        bne @message_loop

    @out:
        ;; Reset attributes for the end of the message.
        bit PPU::m_status
        ldx #$2B
        stx PPU::m_address
        ldx #$D5
        stx PPU::m_address
        lda #0
        sta PPU::m_data
        sta PPU::m_data
        sta PPU::m_data

        rts

    message:
        ;; "YOU "
        .byte $33, $29, $2F, $00
        ;; "ARE "
        .byte $1B, $2C, $1F, $00
        ;; "A "
        .byte $1B, $00
        ;; "SUPER "
        .byte $2D, $2F, $2A, $1F, $2C, $00
        ;; "PLAYER!"
        .byte $2A, $26, $1B, $33, $1F, $2C, $80, $FF
    .endproc
.endscope