This repository has been archived by the owner on Nov 5, 2022. It is now read-only.
forked from DECAfpga/gameboy
-
Notifications
You must be signed in to change notification settings - Fork 1
/
gbc_snd.vhd
1621 lines (1413 loc) · 78.1 KB
/
gbc_snd.vhd
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
661
662
663
664
665
666
667
668
669
670
671
672
673
674
675
676
677
678
679
680
681
682
683
684
685
686
687
688
689
690
691
692
693
694
695
696
697
698
699
700
701
702
703
704
705
706
707
708
709
710
711
712
713
714
715
716
717
718
719
720
721
722
723
724
725
726
727
728
729
730
731
732
733
734
735
736
737
738
739
740
741
742
743
744
745
746
747
748
749
750
751
752
753
754
755
756
757
758
759
760
761
762
763
764
765
766
767
768
769
770
771
772
773
774
775
776
777
778
779
780
781
782
783
784
785
786
787
788
789
790
791
792
793
794
795
796
797
798
799
800
801
802
803
804
805
806
807
808
809
810
811
812
813
814
815
816
817
818
819
820
821
822
823
824
825
826
827
828
829
830
831
832
833
834
835
836
837
838
839
840
841
842
843
844
845
846
847
848
849
850
851
852
853
854
855
856
857
858
859
860
861
862
863
864
865
866
867
868
869
870
871
872
873
874
875
876
877
878
879
880
881
882
883
884
885
886
887
888
889
890
891
892
893
894
895
896
897
898
899
900
901
902
903
904
905
906
907
908
909
910
911
912
913
914
915
916
917
918
919
920
921
922
923
924
925
926
927
928
929
930
931
932
933
934
935
936
937
938
939
940
941
942
943
944
945
946
947
948
949
950
951
952
953
954
955
956
957
958
959
960
961
962
963
964
965
966
967
968
969
970
971
972
973
974
975
976
977
978
979
980
981
982
983
984
985
986
987
988
989
990
991
992
993
994
995
996
997
998
999
1000
library ieee;
use ieee.std_logic_1164.all;
use ieee.std_logic_unsigned.all;
use ieee.numeric_std.all;
library work;
entity gbc_snd is
port (
clk : in std_logic;
ce : in std_logic;
reset : in std_logic;
is_gbc : in std_logic;
s1_read : in std_logic;
s1_write : in std_logic;
s1_addr : in std_logic_vector(6 downto 0);
s1_readdata : out std_logic_vector(7 downto 0);
s1_writedata : in std_logic_vector(7 downto 0);
snd_left : out unsigned(11 downto 0);
snd_right : out unsigned(11 downto 0)
);
end gbc_snd;
architecture SYN of gbc_snd is
subtype wav_t is std_logic_vector(3 downto 0);
type wav_arr_t is array(0 to 31) of wav_t;
signal en_snd : boolean; -- Enable at base sound frequency (4.19MHz)
signal en_snd2 : boolean; -- Enable at clk/2
signal en_snd4 : boolean; -- Enable at clk/4
signal en_512 : boolean; -- 512Hz enable
signal en_snden2 : boolean; -- Enable at clk/2
signal en_snden4 : boolean; -- Enable at clk/4
signal en_len : boolean; -- Sample length
signal en_len_r : boolean;
signal en_env : boolean; -- Envelope
signal en_sweep : boolean; -- Sweep
signal snd_enable : std_logic;
signal sq1_swper : std_logic_vector(2 downto 0); -- Sq1 sweep period
signal sq1_swdir : std_logic; -- Sq1 sweep direction
signal sq1_swdir_change : std_logic; -- Sq1 sweep direction-change
signal sq1_swshift : std_logic_vector(2 downto 0); -- Sq1 sweep frequency shift
signal sq1_duty : std_logic_vector(1 downto 0); -- Sq1 duty cycle
signal sq1_slen : std_logic_vector(6 downto 0); -- Sq1 play length
signal sq1_svol : std_logic_vector(3 downto 0); -- Sq1 initial volume
signal sq1_out : std_logic;
signal sq1_envsgn : std_logic; -- Sq1 envelope sign
signal sq1_envsgn_old : std_logic; -- Sq1 old envelope sign (used in zombie mode)
signal sq1_envper : std_logic_vector(2 downto 0); -- Sq1 envelope period
signal sq1_envper_old : std_logic_vector(2 downto 0); -- Sq1 old envelope period (used in zombie mode)
signal sq1_freq : std_logic_vector(10 downto 0); -- Sq1 frequency
signal sq1_trigger : std_logic; -- Sq1 trigger play note
signal sq1_lenchk : std_logic; -- Sq1 length check enable
signal sq1_len_en_change : std_logic; -- Sq1 length off -> on
signal sq1_fr2 : std_logic_vector(10 downto 0); -- Sq1 frequency (shadow copy)
signal sq1_vol : std_logic_vector(3 downto 0); -- Sq1 initial volume
signal sq1_nr2change : std_logic;
signal sq1_lenchange : std_logic;
signal sq1_lenquirk : std_logic;
signal sq1_freqchange : std_logic;
signal sq1_playing : std_logic; -- Sq1 channel active
signal sq1_wav : std_logic_vector(5 downto 0); -- Sq1 output waveform
signal sq1_suppressed : std_logic; -- Sq1 channel 1st sample suppressed when triggered after poweron
signal sq2_out : std_logic;
signal sq2_duty : std_logic_vector(1 downto 0); -- Sq2 duty cycle
signal sq2_slen : std_logic_vector(6 downto 0); -- Sq2 play length
signal sq2_svol : std_logic_vector(3 downto 0); -- Sq2 initial volume
signal sq2_nr2change : std_logic;
signal sq2_lenchange : std_logic;
signal sq2_lenquirk : std_logic;
signal sq2_envsgn : std_logic; -- Sq2 envelope sign
signal sq2_envper : std_logic_vector(2 downto 0); -- Sq2 envelope period
signal sq2_envsgn_old : std_logic; -- Sq2 old envelope sign (used in zombie mode)
signal sq2_envper_old : std_logic_vector(2 downto 0); -- Sq2 old envelope period (used in zombie mode)
signal sq2_freq : std_logic_vector(10 downto 0); -- Sq2 frequency
signal sq2_trigger : std_logic; -- Sq2 trigger play note
signal sq2_lenchk : std_logic; -- Sq2 length check enable
signal sq2_vol : std_logic_vector(3 downto 0); -- Sq2 initial volume
signal sq2_playing : std_logic; -- Sq2 channel active
signal sq2_wav : std_logic_vector(5 downto 0); -- Sq2 output waveform
signal sq2_suppressed : std_logic; -- Sq2 channel 1st sample suppressed when triggered after poweron
signal wav_enable : std_logic; -- Wave enable
signal wav_slen : std_logic_vector(8 downto 0); -- Wave play length
signal wav_lenchange : std_logic;
signal wav_lenquirk : std_logic;
signal wav_volsh : std_logic_vector(1 downto 0); -- Wave volume shift
signal wav_freq : std_logic_vector(10 downto 0); -- Wave frequency
signal wav_trigger : std_logic; -- Wave trigger play note
signal wav_lenchk : std_logic; -- Wave length check enable
signal wav_index : unsigned(4 downto 0); -- Wave current sample index
signal wav_access : unsigned(1 downto 0); -- Wave table access counter used for DMG
signal wav_playing : std_logic;
signal wav_wav : std_logic_vector(5 downto 0); -- Wave output waveform
signal wav_ram : wav_arr_t; -- Wave table
signal noi_slen : std_logic_vector(6 downto 0);
signal noi_lenchange : std_logic;
signal noi_lenquirk : std_logic;
signal noi_svol : std_logic_vector(3 downto 0);
signal noi_nr2change : std_logic;
signal noi_envsgn : std_logic;
signal noi_envper : std_logic_vector(2 downto 0);
signal noi_envsgn_old : std_logic; -- noi old envelope sign (used in zombie mode)
signal noi_envper_old : std_logic_vector(2 downto 0); -- noi old envelope period (used in zombie mode)
signal noi_freqsh : std_logic_vector(3 downto 0);
signal noi_freqchange : std_logic;
signal noi_short : std_logic;
signal noi_div : std_logic_vector(2 downto 0);
signal noi_trigger : std_logic;
signal noi_lenchk : std_logic;
signal noi_vol : std_logic_vector(3 downto 0); -- Noise initial volume
signal noi_playing : std_logic; -- Noise channel active
signal noi_wav : std_logic_vector(5 downto 0); -- Noise output waveform
signal ch_map : std_logic_vector(7 downto 0);
signal ch_vol : std_logic_vector(7 downto 0);
signal framecnt : integer range 0 to 7 := 0;
signal sq1_trigger_r : std_logic;
signal sq2_trigger_r : std_logic;
signal wav_trigger_r : std_logic;
-- signal noi_trigger_r : std_logic;
begin
en_snd2 <= en_snd and en_snden2;
en_snd4 <= en_snd and en_snden4;
process (clk, en_snd, reset)
begin
if reset = '1' then
en_snd <= false;
elsif rising_edge(clk) then
if ce = '1' then
en_snd <= not en_snd;
end if;
end if;
end process;
-- Calculate divided and frame sequencer clock enables
process (clk, en_snd, snd_enable, reset)
variable clkcnt : unsigned(1 downto 0);
variable cnt_512 : unsigned(12 downto 0);
variable temp_512 : unsigned(13 downto 0);
begin
if reset = '1' or snd_enable = '0' then --only clock frame sequencer if sound is enabled, restart at 0
clkcnt := "00";
cnt_512 := (others => '0');
framecnt <= 0;
en_len_r <= false;
en_snden2 <= false;
en_snden4 <= false;
en_len <= false;
en_env <= false;
en_sweep <= false;
en_512 <= false;
elsif rising_edge(clk) then
if ce = '1' then
-- Base clock divider
if en_snd then
clkcnt := clkcnt + 1;
if clkcnt(0) = '1' then
en_snden2 <= true;
else
en_snden2 <= false;
end if;
if clkcnt = "11" then
en_snden4 <= true;
else
en_snden4 <= false;
end if;
end if;
-- Frame sequencer (length, envelope, sweep) clock enables
en_len <= false;
en_env <= false;
en_sweep <= false;
if en_512 then
en_len_r <= not en_len_r;
if framecnt = 0 or framecnt = 2 or framecnt = 4 or framecnt = 6 then
en_len <= true;
en_len_r <= not en_len_r;
end if;
if framecnt = 2 or framecnt = 6 then
en_sweep <= true;
end if;
if framecnt = 7 then
en_env <= true;
end if;
if framecnt < 7 then
framecnt <= framecnt + 1;
else
framecnt <= 0;
end if;
end if;
--
en_512 <= false;
if en_snd then
temp_512 := ('0' & cnt_512) + to_unsigned(1, temp_512'length);
cnt_512 := temp_512(temp_512'high - 1 downto temp_512'low);
en_512 <= (temp_512(13) = '1');
end if;
end if;
end if;
end process;
-- Registers
registers : process (clk, snd_enable, reset, is_gbc)
variable sq1_trigger_cnt : unsigned(2 downto 0);
variable sq2_trigger_cnt : unsigned(2 downto 0);
variable wav_trigger_cnt : unsigned(3 downto 0);
variable wave_index_write : std_logic_vector(3 downto 0);
begin
if rising_edge(clk) then
-- Registers
if reset = '1' then
-- Reset register values
sq1_swper <= (others => '0');
sq1_swdir <= '0';
sq1_swshift <= (others => '0');
sq1_duty <= (others => '0');
sq1_slen <= (others => '0');
sq1_svol <= (others => '0');
sq1_envsgn <= '0';
sq1_envper <= (others => '0');
sq1_freq <= (others => '0');
sq1_lenchk <= '0';
sq1_trigger <= '0';
sq2_duty <= (others => '0');
sq2_slen <= (others => '0');
sq2_svol <= (others => '0');
sq2_envsgn <= '0';
sq2_envper <= (others => '0');
sq2_freq <= (others => '0');
sq2_lenchk <= '0';
sq2_trigger <= '0';
wav_enable <= '0';
wav_volsh <= (others => '0');
wav_freq <= (others => '0');
wav_trigger <= '0';
wav_lenchk <= '0';
sq1_trigger_cnt := (others => '0'); --counter to delay the trigger
sq2_trigger_cnt := (others => '0'); --counter to delay the trigger
wav_trigger_cnt := (others => '0'); --counter to delay the trigger
noi_slen <= (others => '0');
noi_svol <= (others => '0');
noi_envsgn <= '0';
noi_envper <= (others => '0');
noi_freqsh <= (others => '0');
noi_short <= '0';
noi_div <= (others => '0');
noi_trigger <= '0';
noi_lenchk <= '0';
ch_map <= (others => '0');
ch_vol <= (others => '0');
-- Wave table https://gbdev.gg8.se/wiki/articles/Gameboy_sound_hardware#Power_Control
if is_gbc = '1' then
wav_ram(0) <= X"0";
wav_ram(1) <= X"0";
wav_ram(2) <= X"F";
wav_ram(3) <= X"F";
wav_ram(4) <= X"0";
wav_ram(5) <= X"0";
wav_ram(6) <= X"F";
wav_ram(7) <= X"F";
wav_ram(8) <= X"0";
wav_ram(9) <= X"0";
wav_ram(10) <= X"F";
wav_ram(11) <= X"F";
wav_ram(12) <= X"0";
wav_ram(13) <= X"0";
wav_ram(14) <= X"F";
wav_ram(15) <= X"F";
wav_ram(16) <= X"0";
wav_ram(17) <= X"0";
wav_ram(18) <= X"F";
wav_ram(19) <= X"F";
wav_ram(20) <= X"0";
wav_ram(21) <= X"0";
wav_ram(22) <= X"F";
wav_ram(23) <= X"F";
wav_ram(24) <= X"0";
wav_ram(25) <= X"0";
wav_ram(26) <= X"F";
wav_ram(27) <= X"F";
wav_ram(28) <= X"0";
wav_ram(29) <= X"0";
wav_ram(30) <= X"F";
wav_ram(31) <= X"F";
else
wav_ram(0) <= X"8";
wav_ram(1) <= X"4";
wav_ram(2) <= X"4";
wav_ram(3) <= X"0";
wav_ram(4) <= X"4";
wav_ram(5) <= X"3";
wav_ram(6) <= X"A";
wav_ram(7) <= X"A";
wav_ram(8) <= X"2";
wav_ram(9) <= X"D";
wav_ram(10) <= X"7";
wav_ram(11) <= X"8";
wav_ram(12) <= X"9";
wav_ram(13) <= X"2";
wav_ram(14) <= X"3";
wav_ram(15) <= X"C";
wav_ram(16) <= X"6";
wav_ram(17) <= X"0";
wav_ram(18) <= X"5";
wav_ram(19) <= X"9";
wav_ram(20) <= X"5";
wav_ram(21) <= X"9";
wav_ram(22) <= X"B";
wav_ram(23) <= X"0";
wav_ram(24) <= X"3";
wav_ram(25) <= X"4";
wav_ram(26) <= X"B";
wav_ram(27) <= X"8";
wav_ram(28) <= X"2";
wav_ram(29) <= X"E";
wav_ram(30) <= X"D";
wav_ram(31) <= X"A";
end if;
snd_enable <= '0';
elsif ce = '1' then
-- TODO: align wav and noi triggers
if en_snd2 then
if sq1_trigger_cnt = "000" then
sq1_trigger <= '0';
else
sq1_trigger_cnt := sq1_trigger_cnt - 1;
end if;
if sq2_trigger_cnt = "000" then
sq2_trigger <= '0';
else
sq2_trigger_cnt := sq2_trigger_cnt - 1;
end if;
end if;
if wav_trigger_cnt = "0000" then
wav_trigger <= '0';
else
wav_trigger_cnt := wav_trigger_cnt - 1;
end if;
if en_snd then
noi_trigger <= '0';
end if;
sq2_nr2change <= '0';
sq1_nr2change <= '0';
noi_nr2change <= '0';
noi_freqchange <= '0';
sq2_lenchange <= '0';
sq1_lenchange <= '0';
wav_lenchange <= '0';
noi_lenchange <= '0';
sq1_lenquirk <= '0';
sq2_lenquirk <= '0';
wav_lenquirk <= '0';
noi_lenquirk <= '0';
sq1_swdir_change <= '0';
if sq1_freqchange = '1' then
sq1_freq <= sq1_fr2;
end if;
-- write to registers ignored when the apu is off , Wave memory can be read back freely , NR52 power control is always writable
if s1_write = '1' and (snd_enable = '1' or s1_addr = "0100110" or s1_addr(5 downto 4) = "11" or (is_gbc = '0' and (s1_addr = "0010001" or s1_addr = "0010110" or s1_addr = "0011011" or s1_addr = "0100000" ))) then
case s1_addr is
-- Square 1
when "0010000" => -- NR10 FF10 -PPP NSSS Sweep period, negate, shift
sq1_swper <= s1_writedata(6 downto 4);
if s1_writedata(3) = '0' then -- only neg to pos, 1 -> 0
sq1_swdir_change <= '1';
end if;
sq1_swdir <= s1_writedata(3);
sq1_swshift <= s1_writedata(2 downto 0);
when "0010001" => -- NR11 FF11 DDLL LLLL Duty, Length load (64-L)
if snd_enable = '1' then
sq1_duty <= s1_writedata(7 downto 6);
end if;
sq1_slen <= std_logic_vector("1000000" - unsigned(s1_writedata(5 downto 0)));
sq1_lenchange <= '1';
when "0010010" => -- NR12 FF12 VVVV APPP Starting volume, Envelope add mode, period
-- zombie mode copy old values
sq1_envsgn_old <= sq1_envsgn;
sq1_envper_old <= sq1_envper;
-- write to registers
sq1_nr2change <= '1';
sq1_svol <= s1_writedata(7 downto 4);
sq1_envsgn <= s1_writedata(3);
sq1_envper <= s1_writedata(2 downto 0);
when "0010011" => -- NR13 FF13 FFFF FFFF Frequency LSB
sq1_freq(7 downto 0) <= s1_writedata;
when "0010100" => -- NR14 FF14 TL-- -FFF Trigger, Length enable, Frequency MSB
sq1_trigger <= s1_writedata(7);
if s1_writedata(7) = '1' then
if sq1_playing = '1' then
sq1_trigger_cnt := "010"; -- --according to sameboy : Timing quirk: if already active, sound starts 2 (2MHz) ticks earlier.
else
sq1_trigger_cnt := "100";
end if;
end if;
if sq1_lenchk = '0' and s1_writedata(6) = '1' and en_len_r then
sq1_lenquirk <= '1';
end if;
sq1_lenchk <= s1_writedata(6);
sq1_freq(10 downto 8) <= s1_writedata(2 downto 0);
-- Square 2
when "0010110" => -- NR21 FF16 DDLL LLLL Duty, Length load (64-L)
if snd_enable = '1' then
sq2_duty <= s1_writedata(7 downto 6);
end if;
sq2_slen <= std_logic_vector("1000000" - unsigned(s1_writedata(5 downto 0)));
sq2_lenchange <= '1';
when "0010111" => -- NR22 FF17 VVVV APPP Starting volume, Envelope add mode, period
-- zombie mode copy old values
sq2_envsgn_old <= sq2_envsgn;
sq2_envper_old <= sq2_envper;
-- write to registers
sq2_svol <= s1_writedata(7 downto 4);
sq2_nr2change <= '1';
sq2_envsgn <= s1_writedata(3);
sq2_envper <= s1_writedata(2 downto 0);
when "0011000" => -- NR23 FF18 FFFF FFFF Frequency LSB
sq2_freq(7 downto 0) <= s1_writedata;
when "0011001" => -- NR24 FF19 TL-- -FFF Trigger, Length enable, Frequency MSB
sq2_trigger <= s1_writedata(7);
if s1_writedata(7) = '1' then
if sq2_playing = '1' then
sq2_trigger_cnt := "010"; -- according to sameboy : Timing quirk: if already active, sound starts 2 (2MHz) ticks earlier.
else
sq2_trigger_cnt := "100";
end if;
end if;
if sq2_lenchk = '0' and s1_writedata(6) = '1' and en_len_r then
sq2_lenquirk <= '1';
end if;
sq2_lenchk <= s1_writedata(6);
sq2_freq(10 downto 8) <= s1_writedata(2 downto 0);
-- Wave
when "0011010" => -- NR30 FF1A E--- ---- DAC power
wav_enable <= s1_writedata(7);
when "0011011" => -- NR31 FF1B LLLL LLLL Length load (256-L)
-- wav_slen <= s1_writedata;
wav_slen <= std_logic_vector("100000000" - unsigned(s1_writedata));
wav_lenchange <= '1';
when "0011100" => -- NR32 FF1C -VV- ---- Volume code (00=0%, 01=100%, 10=50%, 11=25%)
wav_volsh <= s1_writedata(6 downto 5);
when "0011101" => -- NR33 FF1D FFFF FFFF Frequency LSB
wav_freq(7 downto 0) <= s1_writedata;
when "0011110" => -- NR34 FF1E TL-- -FFF Trigger, Length enable, Frequency MSB
wav_trigger <= s1_writedata(7);
if s1_writedata(7) = '1' then
wav_trigger_cnt := "1001";
end if;
if wav_lenchk = '0' and s1_writedata(6) = '1' and en_len_r then
wav_lenquirk <= '1';
end if;
wav_lenchk <= s1_writedata(6);
wav_freq(10 downto 8) <= s1_writedata(2 downto 0);
-- Noise
when "0100000" => -- NR41 FF20 --LL LLLL Length load (64-L)
noi_slen <= std_logic_vector("1000000" - unsigned(s1_writedata(5 downto 0)));
noi_lenchange <= '1';
when "0100001" => -- NR42 FF21 VVVV APPP Starting volume, Envelope add mode, period
-- zombie mode copy old values
noi_envsgn_old <= noi_envsgn;
noi_envper_old <= noi_envper;
-- write to registers
noi_svol <= s1_writedata(7 downto 4);
noi_nr2change <= '1';
noi_envsgn <= s1_writedata(3);
noi_envper <= s1_writedata(2 downto 0);
when "0100010" => -- NR43 FF22 SSSS WDDD Clock shift, Width mode of LFSR, Divisor code
noi_freqsh <= s1_writedata(7 downto 4);
noi_short <= s1_writedata(3);
noi_div <= s1_writedata(2 downto 0);
noi_freqchange <= '1';
when "0100011" => -- NR44 FF23 TL-- ---- Trigger, Length enable
noi_trigger <= s1_writedata(7);
if noi_lenchk = '0' and s1_writedata(6) = '1' and en_len_r then
noi_lenquirk <= '1';
end if;
noi_lenchk <= s1_writedata(6);
-- Control/Status
when "0100100" => ch_vol <= s1_writedata; -- NR50 FF24
when "0100101" => ch_map <= s1_writedata; -- NR51 FF25
-- Wave Table
when "0110000" | "0110001" | "0110010" | "0110011" | "0110100" | "0110101" | "0110110" | "0110111" | "0111000" | "0111001" | "0111010" | "0111011" | "0111100" | "0111101" | "0111110" | "0111111" => -- FF30 to FF3F
if wav_playing = '1' then
wave_index_write := std_logic_vector(wav_index(4 downto 1));
else
wave_index_write := s1_addr(3 downto 0);
end if;
if is_gbc = '1' or wav_access > 0 or wav_playing = '0' then
case wave_index_write is
when "0000" => -- FF30 0000 1111 Samples 0 and 1
wav_ram(0) <= s1_writedata(7 downto 4);
wav_ram(1) <= s1_writedata(3 downto 0);
when "0001" => -- FF31 0000 1111 Samples 2 and 3
wav_ram(2) <= s1_writedata(7 downto 4);
wav_ram(3) <= s1_writedata(3 downto 0);
when "0010" => -- FF32 0000 1111 Samples 4 and 5
wav_ram(4) <= s1_writedata(7 downto 4);
wav_ram(5) <= s1_writedata(3 downto 0);
when "0011" => -- FF33 0000 1111 Samples 6 and 31
wav_ram(6) <= s1_writedata(7 downto 4);
wav_ram(7) <= s1_writedata(3 downto 0);
when "0100" => -- FF34 0000 1111 Samples 8 and 31
wav_ram(8) <= s1_writedata(7 downto 4);
wav_ram(9) <= s1_writedata(3 downto 0);
when "0101" => -- FF35 0000 1111 Samples 10 and 11
wav_ram(10) <= s1_writedata(7 downto 4);
wav_ram(11) <= s1_writedata(3 downto 0);
when "0110" => -- FF36 0000 1111 Samples 12 and 13
wav_ram(12) <= s1_writedata(7 downto 4);
wav_ram(13) <= s1_writedata(3 downto 0);
when "0111" => -- FF37 0000 1111 Samples 14 and 15
wav_ram(14) <= s1_writedata(7 downto 4);
wav_ram(15) <= s1_writedata(3 downto 0);
when "1000" => -- FF38 0000 1111 Samples 16 and 17
wav_ram(16) <= s1_writedata(7 downto 4);
wav_ram(17) <= s1_writedata(3 downto 0);
when "1001" => -- FF39 0000 1111 Samples 18 and 19
wav_ram(18) <= s1_writedata(7 downto 4);
wav_ram(19) <= s1_writedata(3 downto 0);
when "1010" => -- FF3A 0000 1111 Samples 20 and 21
wav_ram(20) <= s1_writedata(7 downto 4);
wav_ram(21) <= s1_writedata(3 downto 0);
when "1011" => -- FF3B 0000 1111 Samples 22 and 23
wav_ram(22) <= s1_writedata(7 downto 4);
wav_ram(23) <= s1_writedata(3 downto 0);
when "1100" => -- FF3C 0000 1111 Samples 24 and 25
wav_ram(24) <= s1_writedata(7 downto 4);
wav_ram(25) <= s1_writedata(3 downto 0);
when "1101" => -- FF3D 0000 1111 Samples 26 and 27
wav_ram(26) <= s1_writedata(7 downto 4);
wav_ram(27) <= s1_writedata(3 downto 0);
when "1110" => -- FF3E 0000 1111 Samples 28 and 29
wav_ram(28) <= s1_writedata(7 downto 4);
wav_ram(29) <= s1_writedata(3 downto 0);
when "1111" => -- FF3F 0000 1111 Samples 30 and 31
wav_ram(30) <= s1_writedata(7 downto 4);
wav_ram(31) <= s1_writedata(3 downto 0);
end case;
end if;
-- NR52 FF26 P--- NW21 Power control/status, Channel length statuses
when "0100110" =>
-- TODO: maybe check if event on poweroff or poweron
snd_enable <= s1_writedata(7);
if s1_writedata(7) = '0' then
-- Reset register values
sq1_swper <= (others => '0');
sq1_swdir <= '0';
sq1_swshift <= (others => '0');
sq1_duty <= (others => '0');
sq1_svol <= (others => '0');
sq1_envsgn <= '0';
sq1_envper <= (others => '0');
sq1_freq <= (others => '0');
sq1_lenchk <= '0';
sq1_trigger <= '0';
sq2_duty <= (others => '0');
sq2_svol <= (others => '0');
sq2_envsgn <= '0';
sq2_envper <= (others => '0');
sq2_freq <= (others => '0');
sq2_lenchk <= '0';
sq2_trigger <= '0';
wav_enable <= '0';
wav_volsh <= (others => '0');
wav_freq <= (others => '0');
wav_trigger <= '0';
wav_lenchk <= '0';
sq1_trigger_cnt := (others => '0'); --counter to leave the trigger high for 6 cycles
sq2_trigger_cnt := (others => '0'); --counter to leave the trigger high for 6 cycles
wav_trigger_cnt := (others => '0'); --counter to leave the trigger high for 6 cycles
noi_svol <= (others => '0');
noi_envsgn <= '0';
noi_envper <= (others => '0');
noi_freqsh <= (others => '0');
noi_short <= '0';
noi_div <= (others => '0');
noi_trigger <= '0';
noi_lenchk <= '0';
ch_map <= (others => '0');
ch_vol <= (others => '0');
if is_gbc = '1' then
sq1_slen <= (others => '0');
sq2_slen <= (others => '0');
wav_slen <= (others => '0');
noi_slen <= (others => '0');
end if;
end if;
when others =>
null;
end case;
end if;
end if;
end if;
end process;
process (s1_addr, sq1_swper, sq1_swdir, sq1_swshift, sq1_duty, sq1_svol, sq1_envsgn, sq1_envper, sq1_lenchk,
noi_playing, wav_playing, sq2_playing, sq1_playing, wav_enable, wav_volsh, wav_ram, wav_index, wav_access,
sq2_duty, sq2_svol, sq2_envsgn, sq2_envper, sq2_lenchk, snd_enable, wav_lenchk, noi_svol, noi_envsgn, noi_envper,
noi_freqsh, noi_short, noi_div, noi_lenchk, ch_vol, ch_map, is_gbc, sq1_wav, sq2_wav, wav_wav, noi_wav)
variable wave_index_read : std_logic_vector(3 downto 0);
begin
case s1_addr is
-- Square 1
when "0010000" => -- NR10 FF10 -PPP NSSS Sweep period, negate, shift
s1_readdata <= '1' & sq1_swper & sq1_swdir & sq1_swshift;
when "0010001" => -- NR11 FF11 DDLL LLLL Duty, Length load (64-L)
s1_readdata <= sq1_duty & "111111";
when "0010010" => -- NR12 FF12 VVVV APPP Starting volume, Envelope add mode, period
s1_readdata <= sq1_svol & sq1_envsgn & sq1_envper;
when "0010011" => -- NR13 FF13 FFFF FFFF Frequency LSB
s1_readdata <= X"FF";
when "0010100" => -- NR14 FF14 TL-- -FFF Trigger, Length enable, Frequency MSB
s1_readdata <= '1' & sq1_lenchk & "111111";
-- Square 2
when "0010110" => -- NR21 FF16 DDLL LLLL Duty, Length load (64-L)
s1_readdata <= sq2_duty & "111111";
when "0010111" => -- NR22 FF17 VVVV APPP Starting volume, Envelope add mode, period
s1_readdata <= sq2_svol & sq2_envsgn & sq2_envper;
when "0011000" => -- NR23 FF18 FFFF FFFF Frequency LSB
s1_readdata <= X"FF";
when "0011001" => -- NR24 FF19 TL-- -FFF Trigger, Length enable, Frequency MSB
s1_readdata <= '1' & sq2_lenchk & "111111";
when "0100110" => -- NR52 FF26 P--- NW21 Power control/status, Channel length statuses
s1_readdata <= snd_enable & "111" & noi_playing & wav_playing & sq2_playing & sq1_playing;
-- Wave
when "0011010" => -- NR30 FF1A E--- ---- DAC power
s1_readdata <= wav_enable & "1111111";
when "0011011" => -- NR31 FF1B LLLL LLLL Length load (256-L)
s1_readdata <= X"FF";
when "0011100" => -- NR32 FF1C -VV- ---- Volume code (00=0%, 01=100%, 10=50%, 11=25%)
s1_readdata <= '1' & wav_volsh & "11111";
when "0011101" => -- NR33 FF1D FFFF FFFF Frequency LSB
s1_readdata <= X"FF";
when "0011110" => -- NR34 FF1E TL-- -FFF Trigger, Length enable, Frequency MSB
s1_readdata <= '1' & wav_lenchk & "111111";
-- Noise
when "0100000" => -- NR41 FF20 --LL LLLL Length load (64-L)
s1_readdata <= X"FF";
when "0100001" => -- NR42 FF21 VVVV APPP Starting volume, Envelope add mode, period
s1_readdata <= noi_svol & noi_envsgn & noi_envper;
when "0100010" => -- NR43 FF22 SSSS WDDD Clock shift, Width mode of LFSR, Divisor code
s1_readdata <= noi_freqsh & noi_short & noi_div;
when "0100011" => -- NR44 FF23 TL-- ---- Trigger, Length enable
s1_readdata <= '1' & noi_lenchk & "111111";
-- Wave Table
when "0110000" | "0110001" | "0110010" | "0110011" | "0110100" | "0110101" | "0110110" | "0110111" | "0111000" | "0111001" | "0111010" | "0111011" | "0111100" | "0111101" | "0111110" | "0111111" => -- FF30 to FF3F
if wav_playing = '1' then
wave_index_read := std_logic_vector(wav_index(4 downto 1));
else
wave_index_read := s1_addr(3 downto 0);
end if;
if is_gbc = '1' or wav_access > 0 or wav_playing = '0' then
case wave_index_read is
when "0000" => -- FF30 0000 1111 Samples 0 and 1
s1_readdata <= wav_ram(0) & wav_ram(1);
when "0001" => -- FF31 0000 1111 Samples 2 and 3
s1_readdata <= wav_ram(2) & wav_ram(3);
when "0010" => -- FF32 0000 1111 Samples 4 and 5
s1_readdata <= wav_ram(4) & wav_ram(5);
when "0011" => -- FF33 0000 1111 Samples 6 and 7
s1_readdata <= wav_ram(6) & wav_ram(7);
when "0100" => -- FF34 0000 1111 Samples 8 and 9
s1_readdata <= wav_ram(8) & wav_ram(9);
when "0101" => -- FF35 0000 1111 Samples 10 and 11
s1_readdata <= wav_ram(10) & wav_ram(11);
when "0110" => -- FF36 0000 1111 Samples 12 and 13
s1_readdata <= wav_ram(12) & wav_ram(13);
when "0111" => -- FF37 0000 1111 Samples 14 and 15
s1_readdata <= wav_ram(14) & wav_ram(15);
when "1000" => -- FF38 0000 1111 Samples 16 and 17
s1_readdata <= wav_ram(16) & wav_ram(17);
when "1001" => -- FF39 0000 1111 Samples 18 and 19
s1_readdata <= wav_ram(18) & wav_ram(19);
when "1010" => -- FF3A 0000 1111 Samples 20 and 21
s1_readdata <= wav_ram(20) & wav_ram(21);
when "1011" => -- FF3B 0000 1111 Samples 22 and 23
s1_readdata <= wav_ram(22) & wav_ram(23);
when "1100" => -- FF3C 0000 1111 Samples 24 and 25
s1_readdata <= wav_ram(24) & wav_ram(25);
when "1101" => -- FF3D 0000 1111 Samples 26 and 27
s1_readdata <= wav_ram(26) & wav_ram(27);
when "1110" => -- FF3E 0000 1111 Samples 28 and 29
s1_readdata <= wav_ram(28) & wav_ram(29);
when "1111" => -- FF3F 0000 1111 Samples 30 and 31
s1_readdata <= wav_ram(30) & wav_ram(31);
end case;
else
s1_readdata <= X"FF";
end if;
-- Control/Status
when "0100100" =>
s1_readdata <= ch_vol; -- NR50 FF24
when "0100101" =>
s1_readdata <= ch_map; -- NR51 FF25
-- Undocumented Registers
when "1110110" => -- PCM12 FF76
if is_gbc = '1' then
s1_readdata <= (others => '0');
if sq2_playing = '1' then
s1_readdata(7 downto 4) <= sq2_wav(5 downto 2);
end if;
if sq1_playing = '1' then
s1_readdata(3 downto 0) <= sq1_wav(5 downto 2);
end if;
else
s1_readdata <= X"FF";
end if;
when "1110111" => -- PCM34 FF77
if is_gbc = '1' then
s1_readdata <= (others => '0');
if noi_playing = '1' then
s1_readdata(7 downto 4) <= noi_wav(5 downto 2);
end if;
if wav_playing = '1' then
s1_readdata(3 downto 0) <= wav_wav(5 downto 2);
end if;
else
s1_readdata <= X"FF";
end if;
when others =>
s1_readdata <= X"FF";
end case;
end process;
sound : process (clk, snd_enable, en_snd, en_len, en_env, en_sweep, wav_access, sq1_out, sq1_vol, sq2_out, sq2_vol, noi_vol, sq1_suppressed, sq2_suppressed, reset)
constant duty_0 : std_logic_vector(0 to 7) := "00000001";
constant duty_1 : std_logic_vector(0 to 7) := "10000001";
constant duty_2 : std_logic_vector(0 to 7) := "10000111";
constant duty_3 : std_logic_vector(0 to 7) := "01111110";
variable sq1_fcnt : unsigned(10 downto 0);
variable sq1_phase : integer range 0 to 7;
variable sq1_len : std_logic_vector(6 downto 0);
variable sq1_envcnt : std_logic_vector(3 downto 0); -- Sq1 envelope timer count
variable sq1_swcnt : std_logic_vector(3 downto 0); -- Sq1 sweep timer count
variable sq1_swoffs : unsigned(11 downto 0);
variable sq1_swfr : unsigned(11 downto 0);
variable sq1_sweep_en : boolean;
variable sweep_calculate : boolean;
variable sweep_update : boolean;
variable sweep_negate : boolean;
variable sq1_envoff : boolean; -- check if envelope is on (zombiemode)
variable sq1_sduty : std_logic_vector(1 downto 0); -- Sq1 duty cycle shadow register
variable sq2_fcnt : unsigned(10 downto 0);
variable sq2_phase : integer range 0 to 7;
variable sq2_len : std_logic_vector(6 downto 0);
variable sq2_envcnt : std_logic_vector(3 downto 0); -- Sq2 envelope timer count
variable sq2_envoff : boolean; -- check if envelope is on (zombiemode)
variable sq2_sduty : std_logic_vector(1 downto 0); -- Sq2 duty cycle shadow register
variable wav_fcnt : unsigned(10 downto 0);
variable wav_len : std_logic_vector(8 downto 0);
variable wav_shift_r : boolean;
variable wav_shift : boolean;
variable noi_divisor : unsigned(10 downto 0); -- Noise frequency divisor
variable noi_period : unsigned(10 downto 0); -- Noise period (calculated)
variable noi_fcnt : unsigned(10 downto 0);
variable noi_lfsr : unsigned(14 downto 0); -- 15 bits
variable noi_len : std_logic_vector(6 downto 0);
variable noi_envcnt : std_logic_vector(3 downto 0); -- Noise envelope timer count
variable noi_envoff : boolean; -- check if envelope is on (zombiemode)
variable noi_out : std_logic;
variable noi_xor : std_logic;
variable tmp_volume : unsigned(7 downto 0); -- used in zombie mode
variable acc_fcnt : unsigned(11 downto 0);
begin
if sq1_out = '1' and sq1_suppressed = '0' then
sq1_wav <= sq1_vol & "00";
else
sq1_wav <= "000000";
end if;
if sq2_out = '1' and sq2_suppressed = '0' then
sq2_wav <= sq2_vol & "00";
else
sq2_wav <= "000000";
end if;
if noi_out = '1' then
noi_wav <= noi_vol & "00";
else
noi_wav <= "000000";
end if;
-- Sound processing
if reset = '1' then
sq1_playing <= '0';
sq1_fr2 <= (others => '0');
sq1_fcnt := (others => '0');
sq1_phase := 0;
sq1_vol <= "0000";
sq1_envcnt := "0000";
sq1_swcnt := "0000";
sq1_swoffs := (others => '0');
sq1_swfr := (others => '0');
sq1_out <= '0';
sq2_playing <= '0';
sq2_fcnt := (others => '0');
sq2_phase := 0;
sq2_vol <= "0000";
sq2_envcnt := "0000";
sq2_out <= '0';
sq1_suppressed <= '1';
sq2_suppressed <= '1';
sq1_sduty := (others => '0');
sq2_sduty := (others => '0');
wav_playing <= '0';
wav_fcnt := (others => '0');
wav_shift_r := false;
wav_index <= (others => '0');
wav_access <= (others => '0');
noi_playing <= '0';
noi_fcnt := (others => '0');
noi_lfsr := (others => '1');
noi_vol <= "0000";
noi_envcnt := "0000";
noi_out := '0';
sweep_calculate := false;
sweep_update := false;
sq1_sweep_en := false;
sweep_negate := false;
-- zombie mode check if env is still updating
sq2_envoff := true;
sq1_envoff := true;
noi_envoff := true;
sq1_len := (others => '0');
sq2_len := (others => '0');
wav_len := (others => '0');
noi_len := (others => '0');
sq1_trigger_r <= '0';
sq2_trigger_r <= '0';
wav_trigger_r <= '0';
-- noi_trigger_r <= '0';
elsif rising_edge(clk) then
if ce = '1' then
if sq1_lenchange = '1' then
sq1_len := sq1_slen;
end if;
if sq2_lenchange = '1' then
sq2_len := sq2_slen;
end if;
if noi_lenchange = '1' then
noi_len := noi_slen;
end if;
if wav_lenchange = '1' then
wav_len := wav_slen;
end if;
-- used to detect trigger negedge
sq1_trigger_r <= sq1_trigger;
sq2_trigger_r <= sq2_trigger;
wav_trigger_r <= wav_trigger;
-- noi_trigger_r <= noi_trigger;
if snd_enable = '1' then
if wav_access > 0 then
wav_access <= wav_access - 1;
end if;
----------------------- Square channel 1 -----------------------------
if en_snd4 then
-- Sq1 frequency timer Frequency = 131072/(2048-x) Hz
if sq1_playing = '1' then
acc_fcnt := ('0' & sq1_fcnt) + to_unsigned(1, acc_fcnt'length);
if acc_fcnt(acc_fcnt'high) = '1' then
sq1_suppressed <= '0';
sq1_phase := sq1_phase + 1;
sq1_fcnt := unsigned(sq1_freq);
sq1_sduty := sq1_duty; -- only change duty after the sample is finished
else
sq1_fcnt := acc_fcnt(sq1_fcnt'range);
end if;
end if;
case sq1_sduty is
when "00" => sq1_out <= duty_0(sq1_phase);
when "01" => sq1_out <= duty_1(sq1_phase);
when "10" => sq1_out <= duty_2(sq1_phase);
when "11" => sq1_out <= duty_3(sq1_phase);
when others => null;
end case;
end if;
-- Length counter
if en_len or sq1_lenquirk = '1' then
if sq1_len > 0 and sq1_lenchk = '1' then
sq1_len := std_logic_vector(unsigned(sq1_len) - 1);
end if;
end if;
-- Sweep processing
-- sweep counter
if en_sweep then
sq1_swcnt := std_logic_vector(unsigned(sq1_swcnt) - 1);
if sq1_swcnt = 0 then
-- reload counter with period
if sq1_swper = "000" then
sq1_swcnt := "1000"; -- set to 8
else
sq1_swcnt := '0' & sq1_swper; -- set to period
end if;
-- check if update needed
if sq1_sweep_en and sq1_swper /= "000" then
sweep_calculate := true;
sweep_update := true;
end if;
end if;
end if;
sq1_freqchange <= '0';