1 /* cvu_play_music.c
2    From the function of the same name in the libcvu library for the ColecoVision
3    This file has been included in the tests, since it uses some z80 peephole rules
4    not used elsewhere in the regression tests.
5  */
6 
7 #include <testfwk.h>
8 
9 #ifdef __SDCC
10 #pragma std_c99
11 #endif
12 
13 #pragma disable_warning 85
14 
15 #include <stdint.h>
16 #include <stdbool.h>
17 
18 enum cv_soundchannel {
19   CV_SOUNDCHANNEL_0 = 0x0,
20   CV_SOUNDCHANNEL_1 = 0x2,
21   CV_SOUNDCHANNEL_2 = 0x4,
22   CV_SOUNDCHANNEL_NOISE = 0x6
23 };
24 
25 struct cvu_music
26 {
27 	enum cv_soundchannel channel;
28 	const uint8_t *volume;
29 	const uint16_t *tuning;
30 	uint8_t sixteenth_notes_per_second;
31 	const uint16_t *notes;
32 
33 	uint16_t note_ticks_remaining;
34 	uint16_t pause_ticks_remaining;
35 };
36 
cv_get_vint_frequency(void)37 unsigned char cv_get_vint_frequency(void)
38 {
39 	return(60);
40 }
41 
cv_set_attenuation(enum cv_soundchannel channel,uint8_t dezibel)42 void cv_set_attenuation(enum cv_soundchannel channel, uint8_t dezibel)
43 {
44 }
45 
cv_set_frequency(enum cv_soundchannel channel,uint16_t frequency_divider)46 void cv_set_frequency(enum cv_soundchannel channel, uint16_t frequency_divider)
47 {
48 }
49 
50 const uint16_t CVU_TUNING_ISO16_EQUAL[15] = {54719, 51648, 48749, 46013, 43431, 40993, 38692, 36521, 34471, 32536, 30710, 28987, 0, 0, 0};
51 
52 const uint8_t CVU_VOLUME_DEFAULT[4] = {20, 16, 12, 8};
53 
54 const uint16_t CVU_EMPTY_MUSIC = 0xffff;
55 
cvu_init_music(struct cvu_music * music)56 void cvu_init_music(struct cvu_music *music)
57 {
58 	music->channel = CV_SOUNDCHANNEL_0;
59 	music->volume = CVU_VOLUME_DEFAULT;
60 	music->tuning = CVU_TUNING_ISO16_EQUAL;
61 	music->sixteenth_notes_per_second = 10;
62 	music->note_ticks_remaining = 0;
63 	music->pause_ticks_remaining = 0;
64 	music->notes = &CVU_EMPTY_MUSIC;
65 }
66 
67 #if !defined(__SDCC_pdk14) // Lack of memory
cvu_play_music(struct cvu_music * restrict music)68 bool cvu_play_music(struct cvu_music *restrict music)
69 {
70 	if(music->note_ticks_remaining >= music->sixteenth_notes_per_second)
71 		music->note_ticks_remaining -= music->sixteenth_notes_per_second;
72 	else if(music->pause_ticks_remaining >= music->sixteenth_notes_per_second)
73 	{
74 		cv_set_attenuation(music->channel, 0);
75 		music->pause_ticks_remaining -= music->sixteenth_notes_per_second;
76 	}
77 	else
78 	{
79 		bool pause = false;
80 		const uint16_t note = *(music->notes);
81 
82 		cv_set_attenuation(music->channel, 0);
83 
84 		if(note == 0xffff)
85 			return(false);
86 
87 		// Length calculations:
88 		{
89 			uint8_t length, rel_length;
90 			uint16_t leftover_ticks = music->note_ticks_remaining + music->pause_ticks_remaining; // Avoid desynchronization of multi-voice music.
91 
92 			length = (note >> 4) & 0xf;
93 			if(!length)
94 				length = 0x10;
95 			music->note_ticks_remaining = length * cv_get_vint_frequency();
96 			music->note_ticks_remaining += leftover_ticks;
97 			music->note_ticks_remaining -= music->sixteenth_notes_per_second;
98 
99 			rel_length = (note >> 2) & 0x3;
100 			switch(rel_length)
101 			{
102 			case 0:	// Legato
103 				break;
104 			case 1:	// Staccato
105 				music->pause_ticks_remaining = music->note_ticks_remaining;
106 				music->note_ticks_remaining = music->note_ticks_remaining >> 2;
107 				music->pause_ticks_remaining -= music->note_ticks_remaining;
108 				break;
109 			case 2:
110 				music->pause_ticks_remaining = music->note_ticks_remaining >> 1;
111 				music->note_ticks_remaining -= music->pause_ticks_remaining;
112 				break;
113 			default:	// Standard
114 				music->pause_ticks_remaining = music->note_ticks_remaining >> 2;
115 				music->note_ticks_remaining -= music->pause_ticks_remaining;
116 				break;
117 			}
118 		}
119 
120 		// Frequency calculations:
121 		{
122 			uint8_t octave, halftone;
123 			uint16_t frequency_divider;
124 
125 			halftone = (note >> 8) & 0xf;
126 			pause = (halftone == 0xf);
127 			if(!pause)
128 			{
129 				frequency_divider = music->tuning[halftone];
130 
131 				octave = (note >> 12);
132 				cv_set_frequency(music->channel, (frequency_divider >> octave) <= 32736 ? frequency_divider >> octave : frequency_divider >> (octave + 1));
133 			}
134 		}
135 
136 		// Loudness calculations:
137 		{
138 			cv_set_attenuation(music->channel, pause ? 0 : (music->volume[note & 0x3]));
139 		}
140 		music->notes++;
141 	}
142 	return(true);
143 }
144 #endif
145 
testBug(void)146 void testBug(void)
147 {
148 	struct cvu_music music;
149 	cvu_init_music(&music);
150 #if !defined(__SDCC_pdk14) // Lack of memory
151 	cvu_play_music(&music);
152 #endif
153 }
154 
155