OSDN Git Service

[VM][DEVICE] You should add "DECVICE::initialize()" to top of initialize().
[csp-qt/common_source_project-fm7.git] / source / src / vm / sn76489an.cpp
1 /*
2         Skelton for retropc emulator
3
4         Author : Takeda.Toshiya
5         Date   : 2006.08.18 -
6
7         [ SN76489AN ]
8 */
9
10 #include "sn76489an.h"
11
12 #ifdef HAS_SN76489
13 // SN76489
14 #define NOISE_FB        0x4000
15 #define NOISE_DST_TAP   1
16 #define NOISE_SRC_TAP   2
17 #else
18 // SN76489A, SN76496
19 #define NOISE_FB        0x10000
20 #define NOISE_DST_TAP   4
21 #define NOISE_SRC_TAP   8
22 #endif
23 #define NOISE_MODE      ((regs[6] & 4) ? 1 : 0)
24
25 void SN76489AN::initialize()
26 {
27         DEVICE::initialize();
28         mute = false;
29         cs = we = true;
30 }
31
32 void SN76489AN::reset()
33 {
34         touch_sound();
35         for(int i = 0; i < 4; i++) {
36                 ch[i].count = 0;
37                 ch[i].period = 1;
38                 ch[i].volume = 0;
39                 ch[i].signal = false;
40         }
41         for(int i = 0; i < 8; i += 2) {
42                 regs[i + 0] = 0;
43                 regs[i + 1] = 0x0f;     // volume = 0
44         }
45         noise_gen = NOISE_FB;
46         ch[3].signal = false;
47 }
48
49 void SN76489AN::write_io8(uint32_t addr, uint32_t data)
50 {
51         if(data & 0x80) {
52                 index = (data >> 4) & 7;
53                 int c = index >> 1;
54                 
55                 switch(index & 7) {
56                 case 0: case 2: case 4:
57                         touch_sound();
58                         // tone : frequency
59                         regs[index] = (regs[index] & 0x3f0) | (data & 0x0f);
60                         ch[c].period = regs[index] ? regs[index] : 0x400;
61 //                      ch[c].count = 0;
62                         break;
63                 case 1: case 3: case 5: case 7:
64                         touch_sound();
65                         // tone / noise : volume
66                         regs[index] = data & 0x0f;
67                         ch[c].volume = volume_table[data & 0x0f];
68                         break;
69                 case 6:
70                         touch_sound();
71                         // noise : frequency, mode
72                         regs[6] = data;
73                         data &= 3;
74                         ch[3].period = (data == 3) ? (ch[2].period << 1) : (1 << (data + 5));
75 //                      ch[3].count = 0;
76                         noise_gen = NOISE_FB;
77                         ch[3].signal = false;
78                         break;
79                 }
80         } else {
81                 int c = index >> 1;
82                 
83                 switch(index & 0x07) {
84                 case 0: case 2: case 4:
85                         touch_sound();
86                         // tone : frequency
87                         regs[index] = (regs[index] & 0x0f) | (((uint16_t)data << 4) & 0x3f0);
88                         ch[c].period = regs[index] ? regs[index] : 0x400;
89 //                      ch[c].count = 0;
90                         // update noise shift frequency
91                         if(index == 4 && (regs[6] & 3) == 3) {
92                                 ch[3].period = ch[2].period << 1;
93                         }
94                         break;
95                 }
96         }
97 }
98
99 void SN76489AN::write_signal(int id, uint32_t data, uint32_t mask)
100 {
101         if(id == SIG_SN76489AN_MUTE) {
102                 touch_sound();
103                 mute = ((data & mask) != 0);
104         } else if(id == SIG_SN76489AN_DATA) {
105                 touch_sound();
106                 val = data & mask;
107         } else if(id == SIG_SN76489AN_CS) {
108                 bool next = ((data & mask) != 0);
109                 if(cs != next) {
110                         if(!(cs = next) && !we) {
111                                 write_io8(0, val);
112                         }
113                 }
114         } else if(id == SIG_SN76489AN_CS) {
115                 bool next = ((data & mask) != 0);
116                 if(cs != next) {
117                         cs = next;
118                         if(!cs && !we) {
119                                 write_io8(0, val);
120                         }
121                 }
122         } else if(id == SIG_SN76489AN_WE) {
123                 bool next = ((data & mask) != 0);
124                 if(we != next) {
125                         we = next;
126                         if(!cs && !we) {
127                                 write_io8(0, val);
128                         }
129                 }
130         }
131 }
132
133 void SN76489AN::mix(int32_t* buffer, int cnt)
134 {
135         if(mute) {
136                 return;
137         }
138         for(int i = 0; i < cnt; i++) {
139                 int32_t vol_l = 0, vol_r = 0;
140                 for(int j = 0; j < 4; j++) {
141                         if(!ch[j].volume) {
142                                 continue;
143                         }
144                         bool prev_signal = ch[j].signal;
145                         int prev_count = ch[j].count;
146                         ch[j].count -= diff;
147                         if(ch[j].count < 0) {
148                                 ch[j].count += ch[j].period << 8;
149                                 if(j == 3) {
150                                         if(((noise_gen & NOISE_DST_TAP) ? 1 : 0) ^ (((noise_gen & NOISE_SRC_TAP) ? 1 : 0) * NOISE_MODE)) {
151                                                 noise_gen >>= 1;
152                                                 noise_gen |= NOISE_FB;
153                                         } else {
154                                                 noise_gen >>= 1;
155                                         }
156                                         ch[3].signal = ((noise_gen & 1) != 0);
157                                 } else {
158                                         ch[j].signal = !ch[j].signal;
159                                 }
160                         }
161                         int32_t sample = (prev_signal != ch[j].signal && prev_count < diff) ? (ch[j].volume * (2 * prev_count - diff)) / diff : ch[j].volume;
162                         int32_t vol_tmp_l = apply_volume(sample, volume_l);
163                         int32_t vol_tmp_r = apply_volume(sample, volume_r);
164                         
165                         vol_l += prev_signal ? vol_tmp_l : -vol_tmp_l;
166                         vol_r += prev_signal ? vol_tmp_r : -vol_tmp_r;
167                 }
168                 *buffer++ += vol_l; // L
169                 *buffer++ += vol_r; // R
170         }
171 }
172
173 void SN76489AN::set_volume(int ch, int decibel_l, int decibel_r)
174 {
175         volume_l = decibel_to_volume(decibel_l);
176         volume_r = decibel_to_volume(decibel_r);
177 }
178
179 void SN76489AN::initialize_sound(int rate, int clock, int volume)
180 {
181         // create gain
182         double vol = volume;
183         for(int i = 0; i < 15; i++) {
184                 volume_table[i] = (int)vol;
185                 vol /= 1.258925412;
186         }
187         volume_table[15] = 0;
188         diff = (int)(16.0 * (double)clock / (double)rate + 0.5);
189 }
190
191 #define STATE_VERSION   1
192
193 void SN76489AN::save_state(FILEIO* state_fio)
194 {
195         state_fio->FputUint32(STATE_VERSION);
196         state_fio->FputInt32(this_device_id);
197         
198         state_fio->Fwrite(regs, sizeof(regs), 1);
199         state_fio->FputInt32(index);
200         state_fio->Fwrite(ch, sizeof(ch), 1);
201         state_fio->FputUint32(noise_gen);
202         state_fio->FputBool(mute);
203         state_fio->FputBool(cs);
204         state_fio->FputBool(we);
205         state_fio->FputUint8(val);
206 }
207
208 bool SN76489AN::load_state(FILEIO* state_fio)
209 {
210         if(state_fio->FgetUint32() != STATE_VERSION) {
211                 return false;
212         }
213         if(state_fio->FgetInt32() != this_device_id) {
214                 return false;
215         }
216         state_fio->Fread(regs, sizeof(regs), 1);
217         index = state_fio->FgetInt32();
218         state_fio->Fwrite(ch, sizeof(ch), 1);
219         noise_gen = state_fio->FgetUint32();
220         mute = state_fio->FgetBool();
221         cs = state_fio->FgetBool();
222         we = state_fio->FgetBool();
223         val = state_fio->FgetUint8();
224         //touch_sound();
225         return true;
226 }
227