forked from sinusoid-studios/rhythm-land
-
Notifications
You must be signed in to change notification settings - Fork 0
/
Copy pathjukebox.asm
660 lines (577 loc) · 11.7 KB
/
jukebox.asm
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
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
434
435
436
437
438
439
440
441
442
443
444
445
446
447
448
449
450
451
452
453
454
455
456
457
458
459
460
461
462
463
464
465
466
467
468
469
470
471
472
473
474
475
476
477
478
479
480
481
482
483
484
485
486
487
488
489
490
491
492
493
494
495
496
497
498
499
500
501
502
503
504
505
506
507
508
509
510
511
512
513
514
515
516
517
518
519
520
521
522
523
524
525
526
527
528
529
530
531
532
533
534
535
536
537
538
539
540
541
542
543
544
545
546
547
548
549
550
551
552
553
554
555
556
557
558
559
560
561
562
563
564
565
566
567
568
569
570
571
572
573
574
575
576
577
578
579
580
581
582
583
584
585
586
587
588
589
590
591
592
593
594
595
596
597
598
599
600
601
602
603
604
605
606
607
608
609
610
611
612
613
614
615
616
617
618
619
620
621
622
623
624
625
626
627
628
629
630
631
632
633
634
635
636
637
638
639
640
641
642
643
644
645
646
647
648
649
650
651
652
653
654
655
656
657
658
659
660
INCLUDE "constants/hardware.inc"
INCLUDE "constants/SoundSystem.inc"
INCLUDE "constants/SoundSystemNotes.inc"
INCLUDE "constants/music.inc"
INCLUDE "constants/sfx.inc"
;==============================================================
; starting point
;==============================================================
WaitVRAMAvailable: MACRO
ld hl,rSTAT
.waitvram\@
bit 1,[hl] ; wait for mode 0 or 1
jr nz,.waitvram\@
ENDM
SECTION "Start",ROM0
ScreenJukebox::
ld a,LCDCF_ON|LCDCF_BG8000|LCDCF_BG9800|LCDCF_BGON
ldh [hLCDC],a
ld a,BANK(InitializeGB)
ld [rROMB0], a
call InitializeGB
call InitializeVariables
;--------------------------------------
call SoundSystem_Init
; prepare the sfx
ld bc,BANK(SFX_Table)
ld de,SFX_Table
call SFX_Prepare
; start the first song playing
call ChangeMusic
;--------------------------------------
mainloop:
; check to see if Start is held down
ldh a,[hPressedKeys]
bit PADB_START,a
jr z,.notoggle
; channel toggle
ldh a,[hNewKeys]
ld b,a
ld a,AUDTERM_1_LEFT|AUDTERM_1_RIGHT
bit PADB_UP,b
jr nz,.channeltoggle
ASSERT AUDTERM_2_LEFT|AUDTERM_2_RIGHT == (AUDTERM_1_LEFT|AUDTERM_1_RIGHT) << 1
add a ; shift to channel 2
bit PADB_RIGHT,b
jr nz,.channeltoggle
ASSERT AUDTERM_3_LEFT|AUDTERM_3_RIGHT == (AUDTERM_2_LEFT|AUDTERM_2_RIGHT) << 1
add a ; shift to channel 3
bit PADB_DOWN,b
jr nz,.channeltoggle
ASSERT AUDTERM_4_LEFT|AUDTERM_4_RIGHT == (AUDTERM_3_LEFT|AUDTERM_3_RIGHT) << 1
add a ; shift to channel 4
bit PADB_LEFT,b
jr z,.notoggle
.channeltoggle
; toggle the selected channel's bits in the channel mask
ld hl,wChannelMask
xor [hl]
ld [hl],a
; update immediately
ld a,[wMusicSFXPanning]
and [hl]
ldh [rAUDTERM],a
jr .waitraster
.notoggle
; check to see if A was pressed
ldh a,[hNewKeys]
ASSERT PADB_A == 0
rra ; move bit 0 (PADB_A) to carry
call c,BtnAPressed
; check to see if B was pressed
ldh a,[hNewKeys]
bit PADB_B,a
call nz,BtnBPressed
; check to see if Select was pressed
ldh a,[hNewKeys]
bit PADB_SELECT,a
call nz,BtnSelectPressed
; check to see if right was pressed
ldh a,[hNewKeys]
bit PADB_RIGHT,a
call nz,BtnRightPressed
; check to see if left was pressed
ldh a,[hNewKeys]
bit PADB_LEFT,a
call nz,BtnLeftPressed
; check to see if up was pressed
ldh a,[hNewKeys]
bit PADB_UP,a
call nz,BtnUpPressed
; check to see if down was pressed
ldh a,[hNewKeys]
ASSERT PADB_DOWN == 7
add a ; move bit 7 (PADB_DOWN) to carry
call c,BtnDownPressed
;--------------------------------------
; audio processing
; Note: For the sake of this example showing timing,
; SoundSystem_Process is called at specific time.
; In normal use, you would call it when appropriate.
.waitraster
ldh a,[rLY]
cp 83
jr nz,.waitraster
; set the bg palette
WaitVRAMAvailable
ld a,$E5
ldh [rBGP],a
call SoundSystem_Process
; reset the bg palette
ld a,$E4
ldh [rBGP],a
;--------------------------------------
rst WaitVBlank
; update the ui (i.e. do all the Mode1 tasks first)
call DisplaySongTitle
call DisplayMusicState
call DisplaySync
call DisplaySongID
call DisplaySFXID
call DisplayVUMeters
; update the frame counter
ld hl,wFrameCounter
inc [hl]
jp mainloop
;==============================================================
; Support Routines (Bank 0)
;==============================================================
; set the coords register (de) for use with PrintString
MakeStringXY: MACRO
ld de,(\1 << 8)|\2
ENDM
;--------------------------------------------------------------
ChangeMusic:
call Music_Pause
ld a,[wCurrentSongID]
add a ; * 2
ld hl,SongDataTable
add l
ld l,a
ld a,[hl+]
ld h,[hl]
ld l,a
; put the instrument bank in bc
ld a,[hl+]
ld c,a
ld b,0
; put the insrument table pointer in de
ld a,[hl+]
ld e,a
ld a,[hl+]
ld d,a
push hl
call Music_PrepareInst
pop hl
; put the imusic bank in bc
ld a,[hl+]
ld c,a
ld b,0
; put the music table pointer in de
ld a,[hl+]
ld e,a
ld a,[hl+]
ld d,a
call Music_Play
ret
;--------------------------------------------------------------
ToggleMusic:
ld a,[wMusicPlayState]
and MUSIC_STATE_PLAYING
jr nz,.pause
call Music_Resume
ret
.pause
call Music_Pause
ret
;--------------------------------------------------------------
TriggerSFX:
ld a,[wSFXID]
ld b,a
ld c,MID_C
call SFX_Play
ret
;--------------------------------------------------------------
DisplaySongTitle:
ld h,HIGH(SongTitleTable)
ld a,[wSongID]
add a ; * 2
ld l,a
ld a,[hl+]
ld b,[hl]
ld c,a
MakeStringXY 0,8
call PrintString
ret
;--------------------------------------------------------------
DisplayMusicState:
ld a,[wMusicPlayState]
and MUSIC_STATE_PLAYING
jr nz,.playing
ld bc,StoppedString
jr .display
.playing
ld bc,PlayingString
.display
MakeStringXY 6,14
call PrintString
ret
StoppedString:
DB "Stop",$00
PlayingString:
DB "Play",$00
;--------------------------------------------------------------
DisplaySync:
ld h,HIGH(HexTable)
ld de,$99E7
ld a,[wMusicSyncData]
ld b,a
; left digit
swap a
and $0F
ld l,a
ld a,[hl]
ld [de],a
; right digit
inc e
ld a,b
and $0F
ld l,a
ld a,[hl]
ld [de],a
ret
PUSHS
SECTION "Hex Table",ROM0,ALIGN[8]
HexTable:
DB "0123456789ABCDEF"
POPS
;--------------------------------------------------------------
DisplaySongID:
ld a,[wCurrentSongID]
add $30
ld [$9A07],a
ret
;--------------------------------------------------------------
DisplaySFXID:
ld a,[wSFXID]
add $30
ld [$9A27],a
ret
;--------------------------------------------------------------
DisplayVUMeters:
; channel 1
; $99EC/$9A0C
ld a,[wVUMeter1]
ld b,a
and $08
jr nz,.split1
; top half
xor a
ld [$99EC],a
; bottom half
ld a,b
ld [$9A0C],a
jr .channel2
.split1
; top half
ld a,b
sub 8
ld [$99EC],a
; bottom half
ld a,8
ld [$9A0C],a
; channel 2
; $99EE/$9A0E
.channel2
ld a,[wVUMeter2]
ld b,a
and $08
jr nz,.split2
; top half
xor a
ld [$99EE],a
; bottom half
ld a,b
ld [$9A0E],a
jr .channel3
.split2
; top half
ld a,b
sub 8
ld [$99EE],a
; bottom half
ld a,8
ld [$9A0E],a
; channel 3
; $99F0/$9A10
.channel3
ld a,[wVUMeter3]
ld b,a
and $08
jr nz,.split3
; top half
xor a
ld [$99F0],a
; bottom half
ld a,b
ld [$9A10],a
jr .channel4
.split3
; top half
ld a,b
sub 8
ld [$99F0],a
; bottom half
ld a,8
ld [$9A10],a
; channel 4
; $99F2/$9A12
.channel4
ld a,[wVUMeter4]
ld b,a
and $08
jr nz,.split4
; top half
xor a
ld [$99F2],a
; bottom half
ld a,b
ld [$9A12],a
ret
.split4
; top half
ld a,b
sub 8
ld [$99F2],a
; bottom half
ld a,8
ld [$9A12],a
ret
;--------------------------------------------------------------
; print a null-terminated string in bc at d,e
; d,e are screen coords in tiles
PrintString:
; set the y coord
ld h,0
ld l,e
add hl,hl ; * 2
add hl,hl ; * 4
add hl,hl ; * 8
add hl,hl ; * 16
add hl,hl ; * 32
ld a,$98
add h
ld h,a
; set the x coord
ld a,l
add d
ld l,a
; copy the characters to vram
.charloop
ld a,[bc]
or a ; is this the NULL terminator?
ret z ; yes, exit
ld [hl+],a
inc bc
jr .charloop
;--------------------------------------------------------------
ReadJoypad:
; read D-Pad
ld a,P1F_GET_DPAD
call .readNibble
swap a ; move directions to high nibble
ld b,a
; read buttons
ld a,P1F_GET_BTN
call .readNibble
xor b ; combine buttons and directions + complement
ld b,a
; update hNewKeys
ldh a,[hPressedKeys]
xor b ; a = keys that changed state
and b ; a = keys that changed to pressed
ldh [hNewKeys],a
ld a,b
ldh [hPressedKeys],a
; done reading
ld a,P1F_GET_NONE
ldh [rP1],a
ret
; @param a Value to write to rP1
; @return a Reading from rP1, ignoring non-input bits (forced high)
.readNibble
ldh [rP1],a
; burn 16 cycles between write and read
call .ret ; 6+4 cycles
ldh a,[rP1] ; 3 cycles
ldh a,[rP1] ; 3 cycles
ldh a,[rP1] ; read
or $F0 ; ignore non-input bits
.ret
ret
;==============================================================
; Button press handlers
;==============================================================
BtnAPressed:
call ToggleMusic
ret
BtnBPressed:
call TriggerSFX
ret
BtnSelectPressed:
ASSERT wSongID == wCurrentSongID + 1 ; make sure wSongID follows wCurrentSongID
ld hl,wSongID
ld a,[hl-] ; get hSongID
; set the current song id
ld [hl],a
call ChangeMusic
ret
BtnRightPressed:
ld hl,wSongID
ld a,[hl]
inc a
cp NUM_SONGS
jr nz,.store
xor a
.store
ld [hl],a
ret
BtnLeftPressed:
ld hl,wSongID
ld a,[hl]
dec a
cp -1
jr nz,.store
ld a,NUM_SONGS-1
.store
ld [hl],a
ret
BtnUpPressed:
ld hl,wSFXID
ld a,[hl]
inc a
cp NUM_SFX
jr nz,.store
xor a
.store
ld [hl],a
ret
BtnDownPressed:
ld hl,wSFXID
ld a,[hl]
dec a
cp -1
jr nz,.store
ld a,NUM_SFX-1
.store
ld [hl],a
ret
;==============================================================
; Support Routines (Bank 1)
;==============================================================
; wait for the start of the next V-Blank
WaitVBlankStart: MACRO
.waitvbl\@
ldh a,[rLY]
cp SCRN_Y
jr nz,.waitvbl\@
ENDM
SECTION "Bank 1 Routines",ROMX
InitializeGB:
di
;--------------------------------------
; turn off the screen after entering a vblank
WaitVBlankStart
;--------------------------------------
; clear LCD control registers and disable audio
xor a
ldh [rLCDC],a
ldh [rIE],a
ldh [rIF],a
ldh [rSCX],a
ldh [rSCY],a
ldh [rSTAT],a
ldh [rAUDENA],a ; disable the audio
;--------------------------------------
; initialize the window posision to 255,255
dec a
ldh [rWY],a
ldh [rWX],a
;--------------------------------------
; set the bg palette
ld a,$E4
ldh [rBGP],a
;--------------------------------------
; copy the font tiles to vram
ld de,FontTiles ; source
ld hl,_VRAM8000 ; dest
ld bc,(FontTilesEnd - FontTiles) ; num bytes
call CopyMem
;--------------------------------------
; copy the ui map to vram
ld de,UIMap ; source
ld hl,_SCRN0 ; dest
ld bc,(UIMapEnd - UIMap) ; num bytes
call CopyMem
ld bc,SoundSystem_Version
ld de,$00
call PrintString
;--------------------------------------
; turn on the display
ld a,LCDCF_ON|LCDCF_BG8000|LCDCF_BG9800|LCDCF_OBJOFF|LCDCF_BGON
ldh [rLCDC],a
WaitVBlankStart
;--------------------------------------
; enable the interrupts
ld a,IEF_VBLANK
ldh [rIE],a
xor a
ei
ldh [rIF],a
ret
FontTiles:
INCBIN "data/jukebox-font.bin"
FontTilesEnd:
UIMap:
DB " "
DB " "
DB " A Music Toggle "
DB " ",$EB,"/",$EC," Change Song ID "
DB " Sel Select Song "
DB " ",$E9,"/",$EA," Change SFX ID "
DB " B Play SFX "
DB " "
DB " "
DB $DA,"Timing",$D9,$CC,$CC,$CC,$CC,$CC,$CC,$CC,$CC,$CC,$CC,$CC,$DA," "
DB " "
DB " "
DB " "
DB $DA,"State",$D9,$CC,$CC,$CC,$B7,$DA,"VUM",$D9,$CC,$CC,$CC,$CC," "
DB "Music: ",$CD," "
DB " Sync:$ ",$CD," "
DB " Song:0 ",$CD," "
DB " SFX:0 ",$CD," "
UIMapEnd:
;--------------------------------------------------------------
InitializeVariables:
ld hl,wVBlankDone
xor a
ld [hl+],a ; wVBlankDone
ld [hl+],a ; wFrameCounter
ld [hl+],a ; wCurrentSongID
ld [hl+],a ; wSongID
ld [hl+],a ; wSFXID
ldh [hPressedKeys],a
ldh [hNewKeys],a
ret
;--------------------------------------------------------------
; copy bc bytes from de to hl
; no need to wait for STAT, the display is off
CopyMem:
ld a,[de]
ld [hl+],a
inc de
dec bc
ld a,b
or c
jr nz,CopyMem
ret
;==============================================================
; work ram
;==============================================================
SECTION "Variables",WRAM0
wVBlankDone: DS 1
wFrameCounter: DS 1
wCurrentSongID: DS 1
wSongID: DS 1
wSFXID: DS 1