OSDN Git Service

[VM][STATE] Apply new state framework to some devices a lot (excepts some devices...
[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         if(osd->check_feature(_T("HAS_SN76489"))) {
29                 _NOISE_FB = 0x4000;
30                 _NOISE_DST_TAP = 1;
31                 _NOISE_SRC_TAP = 1;
32         } else {
33                 _NOISE_FB = 0x10000;
34                 _NOISE_DST_TAP = 4;
35                 _NOISE_SRC_TAP = 8;
36                 set_device_name(_T("SN76489AN PSG"));
37         }
38         mute = false;
39         cs = we = true;
40 }
41
42 void SN76489AN::reset()
43 {
44         touch_sound();
45         for(int i = 0; i < 4; i++) {
46                 ch[i].count = 0;
47                 ch[i].period = 1;
48                 ch[i].volume = 0;
49                 ch[i].signal = false;
50         }
51         for(int i = 0; i < 8; i += 2) {
52                 regs[i + 0] = 0;
53                 regs[i + 1] = 0x0f;     // volume = 0
54         }
55         noise_gen = _NOISE_FB;
56         ch[3].signal = false;
57 }
58
59 void SN76489AN::write_io8(uint32_t addr, uint32_t data)
60 {
61         if(data & 0x80) {
62                 index = (data >> 4) & 7;
63                 int c = index >> 1;
64                 
65                 switch(index & 7) {
66                 case 0: case 2: case 4:
67                         touch_sound();
68                         // tone : frequency
69                         regs[index] = (regs[index] & 0x3f0) | (data & 0x0f);
70                         ch[c].period = regs[index] ? regs[index] : 0x400;
71 //                      ch[c].count = 0;
72                         break;
73                 case 1: case 3: case 5: case 7:
74                         touch_sound();
75                         // tone / noise : volume
76                         regs[index] = data & 0x0f;
77                         ch[c].volume = volume_table[data & 0x0f];
78                         break;
79                 case 6:
80                         touch_sound();
81                         // noise : frequency, mode
82                         regs[6] = data;
83                         data &= 3;
84                         ch[3].period = (data == 3) ? (ch[2].period << 1) : (1 << (data + 5));
85 //                      ch[3].count = 0;
86                         noise_gen = _NOISE_FB;
87                         ch[3].signal = false;
88                         break;
89                 }
90         } else {
91                 int c = index >> 1;
92                 
93                 switch(index & 0x07) {
94                 case 0: case 2: case 4:
95                         touch_sound();
96                         // tone : frequency
97                         regs[index] = (regs[index] & 0x0f) | (((uint16_t)data << 4) & 0x3f0);
98                         ch[c].period = regs[index] ? regs[index] : 0x400;
99 //                      ch[c].count = 0;
100                         // update noise shift frequency
101                         if(index == 4 && (regs[6] & 3) == 3) {
102                                 ch[3].period = ch[2].period << 1;
103                         }
104                         break;
105                 }
106         }
107 }
108
109 void SN76489AN::write_signal(int id, uint32_t data, uint32_t mask)
110 {
111         if(id == SIG_SN76489AN_MUTE) {
112                 touch_sound();
113                 mute = ((data & mask) != 0);
114         } else if(id == SIG_SN76489AN_DATA) {
115                 touch_sound();
116                 val = data & mask;
117         } else if(id == SIG_SN76489AN_CS) {
118                 bool next = ((data & mask) != 0);
119                 if(cs != next) {
120                         if(!(cs = next) && !we) {
121                                 write_io8(0, val);
122                         }
123                 }
124         } else if(id == SIG_SN76489AN_CS) {
125                 bool next = ((data & mask) != 0);
126                 if(cs != next) {
127                         cs = next;
128                         if(!cs && !we) {
129                                 write_io8(0, val);
130                         }
131                 }
132         } else if(id == SIG_SN76489AN_WE) {
133                 bool next = ((data & mask) != 0);
134                 if(we != next) {
135                         we = next;
136                         if(!cs && !we) {
137                                 write_io8(0, val);
138                         }
139                 }
140         }
141 }
142
143 void SN76489AN::mix(int32_t* buffer, int cnt)
144 {
145         if(mute) {
146                 return;
147         }
148         for(int i = 0; i < cnt; i++) {
149                 int32_t vol_l = 0, vol_r = 0;
150                 for(int j = 0; j < 4; j++) {
151                         if(!ch[j].volume) {
152                                 continue;
153                         }
154                         bool prev_signal = ch[j].signal;
155                         int prev_count = ch[j].count;
156                         ch[j].count -= diff;
157                         if(ch[j].count < 0) {
158                                 ch[j].count += ch[j].period << 8;
159                                 if(j == 3) {
160                                         if(((noise_gen & _NOISE_DST_TAP) ? 1 : 0) ^ (((noise_gen & _NOISE_SRC_TAP) ? 1 : 0) * NOISE_MODE)) {
161                                                 noise_gen >>= 1;
162                                                 noise_gen |= _NOISE_FB;
163                                         } else {
164                                                 noise_gen >>= 1;
165                                         }
166                                         ch[3].signal = ((noise_gen & 1) != 0);
167                                 } else {
168                                         ch[j].signal = !ch[j].signal;
169                                 }
170                         }
171                         int32_t sample = (prev_signal != ch[j].signal && prev_count < diff) ? (ch[j].volume * (2 * prev_count - diff)) / diff : ch[j].volume;
172                         int32_t vol_tmp_l = apply_volume(sample, volume_l);
173                         int32_t vol_tmp_r = apply_volume(sample, volume_r);
174                         
175                         vol_l += prev_signal ? vol_tmp_l : -vol_tmp_l;
176                         vol_r += prev_signal ? vol_tmp_r : -vol_tmp_r;
177                 }
178                 *buffer++ += vol_l; // L
179                 *buffer++ += vol_r; // R
180         }
181 }
182
183 void SN76489AN::set_volume(int ch, int decibel_l, int decibel_r)
184 {
185         volume_l = decibel_to_volume(decibel_l);
186         volume_r = decibel_to_volume(decibel_r);
187 }
188
189 void SN76489AN::initialize_sound(int rate, int clock, int volume)
190 {
191         // create gain
192         double vol = volume;
193         for(int i = 0; i < 15; i++) {
194                 volume_table[i] = (int)vol;
195                 vol /= 1.258925412;
196         }
197         volume_table[15] = 0;
198         diff = (int)(16.0 * (double)clock / (double)rate + 0.5);
199 }
200
201 #define STATE_VERSION   1
202
203 #include "../statesub.h"
204
205 void SN76489AN::decl_state()
206 {
207         enter_decl_state(STATE_VERSION);
208
209         DECL_STATE_ENTRY_1D_ARRAY(regs, sizeof(regs) / sizeof(uint16_t));
210         DECL_STATE_ENTRY_INT32(index);
211         for(int i = 0; i < 4; i++) {
212                 DECL_STATE_ENTRY_INT32_MEMBER((ch[i].count), i);
213                 DECL_STATE_ENTRY_INT32_MEMBER((ch[i].period), i);
214                 DECL_STATE_ENTRY_INT32_MEMBER((ch[i].volume), i);
215                 DECL_STATE_ENTRY_BOOL_MEMBER((ch[i].signal), i);
216         }
217 //              state_fio->Fwrite(ch, sizeof(ch), 1);
218         DECL_STATE_ENTRY_UINT32(noise_gen);
219         DECL_STATE_ENTRY_BOOL(mute);
220         DECL_STATE_ENTRY_BOOL(cs);
221         DECL_STATE_ENTRY_BOOL(we);
222         DECL_STATE_ENTRY_UINT8(val);
223
224         leave_decl_state();
225 }
226
227 void SN76489AN::save_state(FILEIO* state_fio)
228 {
229         if(state_entry != NULL) {
230                 state_entry->save_state(state_fio);
231         }
232         
233 //      state_fio->FputUint32(STATE_VERSION);
234 //      state_fio->FputInt32(this_device_id);
235         
236 //      state_fio->Fwrite(regs, sizeof(regs), 1);
237 //      state_fio->FputInt32(index);
238 //      state_fio->Fwrite(ch, sizeof(ch), 1);
239 //      state_fio->FputUint32(noise_gen);
240 //      DECL_STATE_ENTRY_BOOL(mute);
241 //      DECL_STATE_ENTRY_BOOL(cs);
242 //      DECL_STATE_ENTRY_BOOL(we);
243 //      state_fio->FputUint8(val);
244 }
245
246 bool SN76489AN::load_state(FILEIO* state_fio)
247 {
248         bool mb = false;
249         if(state_entry != NULL) {
250                 mb = state_entry->load_state(state_fio);
251         }
252         if(!mb) return false;
253
254 //      if(state_fio->FgetUint32() != STATE_VERSION) {
255 //              return false;
256 //      }
257 //      if(state_fio->FgetInt32() != this_device_id) {
258 //              return false;
259 //      }
260 //      state_fio->Fread(regs, sizeof(regs), 1);
261 //      index = state_fio->FgetInt32();
262 //      state_fio->Fread(ch, sizeof(ch), 1);
263 //      noise_gen = state_fio->FgetUint32();
264 //      mute = state_fio->FgetBool();
265 //      cs = state_fio->FgetBool();
266 //      we = state_fio->FgetBool();
267 //      val = state_fio->FgetUint8();
268         //touch_sound();
269         return true;
270 }
271