summaryrefslogtreecommitdiff
path: root/home/joypad.asm
blob: af38bc71097eee4736c0e3995904c2f57a3b45de (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
INCLUDE "constants.asm"


SECTION "home/joypad.asm", ROM0

ClearJoypad::
	xor a
; Pressed this frame (delta)
	ldh [hJoyDown], a
; Currently pressed
	ldh [hJoyState], a
	ret


Joypad::
; Read the joypad register and translate it to something more
; workable for use in-game. There are 8 buttons, so we can use
; one byte to contain all player input.

; Updates:

; hJoypadUp: released this frame (delta)
; hJoypadDown: pressed this frame (delta)
; hJoypadState: currently pressed
; hJoypadSum: pressed so far
	ld a, [$d4ab]
	and $d0
	ret nz
	ld a, 1 << 5 ; select direction keys
	ldh [rJOYP], a
	ldh a, [rJOYP]
	ldh a, [rJOYP]
	cpl
	and $0f
	swap a
	ld b, a
	ld a, 1 << 4 ; select button keys
	ldh [rJOYP], a
	ldh a, [rJOYP]
	ldh a, [rJOYP]
	ldh a, [rJOYP]
	ldh a, [rJOYP]
	ldh a, [rJOYP]
	ldh a, [rJOYP]
	cpl
	and $0f
	or b
	ld b, a
	ld a, (1 << 5 | 1 << 4) ; port reset
	ldh [rJOYP], a
	ldh a, [hJoypadState]
	ld e, a
	xor b
	ld d, a
	and e
	ldh [hJoypadUp], a
	ld a, d
	and b
	ldh [hJoypadDown], a
	ld c, a
	ldh a, [hJoypadSum]
	or c
	ldh [hJoypadSum], a
	ld a, b
	ldh [hJoypadState], a
	ldh [hJoypadState2], a
	; Soft-Reset by holding A+B+SELECT+START
	and (A_BUTTON | B_BUTTON | SELECT | START)
	cp (A_BUTTON | B_BUTTON | SELECT | START)
	jp z, Reset
	ret

GetJoypad::
; Update mirror joypad input from hJoypadState (real input)

; hJoyReleased, hJoyDown and hJoyState are synchronized
; copies of their hJoypad* counterparts.

; bit 0 A
;     1 B
;     2 SELECT
;     3 START
;     4 RIGHT
;     5 LEFT
;     6 UP
;     7 DOWN
	push af
	push hl
	push de
	ld hl, wJoypadFlags
	set 6, [hl]           ; mutex
	ld hl, hJoypadDown
	ld de, hJoyDown
	ld a, [hli]
	ld [de], a
	inc de
	ld a, [hli]
	ld [de], a
	inc de
	ld a, [hl]
	ld [de], a
	ld hl, wJoypadFlags
	res 6, [hl]
	pop de
	pop hl
	pop af
	ret

JoyTitleScreenInput::
; Check if any of the following conditions
; is met for c frames
; - B, Select and Up keys are pressed in same frame
; - A is pressed
; - START is pressed
;
; Inputs: c - number of frames to check for
; Return: carry set if condition met, else reset
.loop
	call DelayFrame
	push bc
	call GetJoypadDebounced
	pop bc
	ldh a, [hJoyState]
	cp (D_UP | SELECT | B_BUTTON)
	jr z, .done
	ldh a, [hJoySum]
	and (START | A_BUTTON)
	jr nz, .done
	dec c
	jr nz, .loop
	and a
	ret
.done
	scf
	ret

GetJoypadDebounced::
; Update hJoySum joypad input from either hJoyDown or
; hJoyState depending on hJoyDebounceSrc.
; hJoyState is only updated every 5 frames and
; the update is delayed by 15 frames after any button
; press.
	call GetJoypad
	ldh a, [hJoyDebounceSrc]
	and a
	ldh a, [hJoyDown]
	jr z, .joyDownSrc
.joyStateSrc
	ldh a, [hJoyState]
.joyDownSrc
	ldh [hJoySum], a
	ldh a, [hJoyDown]
	and a
	jr z, .sampleAfterPress
	ld a, $0f
	ld [wVBlankJoyFrameCounter], a
	ret
.sampleAfterPress
	ld a, [wVBlankJoyFrameCounter]
	and a
	jr z, .sampleRegular
	xor a
	ldh [hJoySum], a
	ret
.sampleRegular
	ld a, $05
	ld [wVBlankJoyFrameCounter], a
	ret

TextboxWaitPressAorB_BlinkCursor:
; Show a blinking cursor in the lower right-hand
; corner of a textbox and wait until A or B is
; pressed.
;
; CAUTION: The cursor has to be shown when calling
; this function or no cursor will be shown at all.
; Waiting on button presses is unaffected by this.
	ldh a, [hSpriteWidth]  ; hTextBoxCursorBlinkInterval is shared with
	push af                ; hSpriteWidth and hSpriteHeight, so we need
	ldh a, [hSpriteHeight] ; to back them up
	push af
	xor a
	ldh [hTextBoxCursorBlinkInterval], a
	ld a, $06
	ldh [hTextBoxCursorBlinkInterval + 1], a ; initially, $600 iterations
.loop
	push hl
	coord hl, (TEXTBOX_WIDTH - 2), (TEXTBOX_Y + TEXTBOX_HEIGHT - 1)
	call TextboxBlinkCursor
	pop hl
	call GetJoypadDebounced
	ldh a, [hJoySum]
	and (A_BUTTON | B_BUTTON)
	jr z, .loop
	pop af
	ldh [hSpriteHeight], a
	pop af
	ldh [hSpriteWidth], a
	ret

ButtonSound::
	ld a, [wLinkMode]
	cp $03
	jr z, .link
	call WaitAorB_BlinkCursor
	push de
	ld de, $5
	call PlaySFX
	pop de
	ret
.link
	ld c, $41
	jp DelayFrames

WaitAorB_BlinkCursor::
.loop
	call BlinkCursor
	call GetJoypadDebounced
	ldh a, [hJoySum]
	and (A_BUTTON | B_BUTTON)
	ret nz
	call UpdateTime
	call UpdateTimeOfDayPalettes
	ld a, $01
	ldh [hBGMapMode], a
	call DelayFrame
	jr .loop

BlinkCursor:
; Show a blinking cursor in the lower right-hand
; corner of the screen
; Will toggle between cursor and blank every
; 16 frames.
	ldh a, [hVBlankCounter]
	and $10
	jr z, .cursor_off
	ld a, "▼"
	jr .save_cursor_state
.cursor_off
	ld a, " "
.save_cursor_state
	ldcoord_a (SCREEN_WIDTH - 2), (SCREEN_HEIGHT - 1)
	ret

TextboxBlinkCursor::
; Show a blinking cursor at the specified position
; that toggles between down arrow and horizontal textbox
; frame tile.
; hl - address of cursor
; hTextBoxCursorBlinkInterval - initial delay between toggling
;                               subsequent delays will be $6FF
;                               calls of this function
; CAUTION: if the cursor is not shown initially, even initial
; hTextBoxCursorBlinkInterval values will cause no cursor
; to be shown at all.
	ld a, [hl]
	ld b, a
	ld a, "▼"
	cp b
	jr nz, .showCursorCountdown
.showTextboxFrameCountdown
	ldh a, [hTextBoxCursorBlinkInterval]
	dec a
	ldh [hTextBoxCursorBlinkInterval], a
	ret nz
	ldh a, [hTextBoxCursorBlinkInterval + 1]
	dec a
	ldh [hTextBoxCursorBlinkInterval + 1], a
	ret nz
	ld a, "─"
	ld [hl], a
	ld a, $ff
	ldh [hTextBoxCursorBlinkInterval], a
	ld a, $06
	ldh [hTextBoxCursorBlinkInterval + 1], a ; reset to $6FF iterations
	ret
.showCursorCountdown
	ldh a, [hTextBoxCursorBlinkInterval]
	and a
	ret z
	dec a
	ldh [hTextBoxCursorBlinkInterval], a
	ret nz
	dec a
	ldh [hTextBoxCursorBlinkInterval], a
	ldh a, [hTextBoxCursorBlinkInterval + 1]
	dec a
	ldh [hTextBoxCursorBlinkInterval + 1], a
	ret nz
	ld a, $06
	ldh [hTextBoxCursorBlinkInterval + 1], a ; reset to $6FF iterations
	ld a, "▼"
	ld [hl], a
	ret