OSDN Git Service

[VM][FMTOWNS][AD7820KR] Improve around sound recording.
[csp-qt/common_source_project-fm7.git] / source / src / vm / fmtowns / adpcm.cpp
1 /*
2         FUJITSU FM Towns Emulator 'eFMTowns'
3
4         Author : Kyuma.Ohta <whatisthis.sowhat _at_ gmail.com>
5         Date   : 2019.01.29-
6
7         [I/O around ADPCM]
8 */
9
10 #include "./adpcm.h"
11 #include "rf5c68.h"
12 #include "ad7820kr.h"
13
14 namespace FMTOWNS {
15
16 #define EVENT_ADC_CLOCK   1
17 #define EVENT_ADPCM_CLOCK 2
18         
19 void ADPCM::initialize()
20 {
21         adc_fifo = new FIFO(64); // OK?
22         for(int i = 0; i < 8; i++) {
23                 dac_int_mask[i] = true; // true = enable intrrupt.
24                 dac_intr[i] = false;
25         }
26         intr_opx = false;
27         event_adc_clock = -1;
28         initialize_adc_clock(-1);
29         event_adpcm_clock = -1;
30 }
31
32 void ADPCM::release()
33 {
34         adc_fifo->release();
35         delete adc_fifo;
36 }
37         
38 void ADPCM::reset()
39 {
40         // Is clear FIFO?
41         adc_fifo->clear();
42         initialize_adc_clock(-1);
43         if(event_adpcm_clock >= 0) {
44                 cancel_event(this, event_adpcm_clock);
45         }
46         register_event(this, EVENT_ADPCM_CLOCK, 16.0e6 / (384.0 * 2.0), true, &event_adpcm_clock); // Is this true?
47 }
48
49 void ADPCM::initialize_adc_clock(int freq)
50 {
51         if(freq <= 0) {
52                 freq = (int)d_adc->read_signal(SIG_AD7820_SAMPLE_RATE);
53         }
54         if(event_adc_clock >= 0) {
55                 cancel_event(this, event_adc_clock);
56         }
57         d_adc->write_signal(SIG_AD7820_DATA_REG, 0x00, 0x00);
58         d_adc->write_signal(SIG_AD7820_CS, 0xffffffff, 0xffffffff);
59         d_adc->write_signal(SIG_AD7820_WR_CONVERSION_MODE, 0, 0xffffffff); // READ MODE..
60         register_event(this, EVENT_ADC_CLOCK, 1.0e6 / (double)freq, true, &event_adc_clock);
61 }               
62
63 void ADPCM::event_callback(int id, int err)
64 {
65         switch(id) {
66         case EVENT_ADC_CLOCK:
67                 d_adc->write_signal(SIG_AD7820_WR_CONVERSION_MODE, 0, 0xffffffff); // READ MODE
68                 d_adc->write_signal(SIG_AD7820_CS, 0xffffffff, 0xffffffff);
69                 d_adc->read_data8(SIG_AD7820_DATA_REG); // Dummy read, start to  sample.
70                 break;
71         case EVENT_ADPCM_CLOCK:
72                 d_rf5c68->write_signal(SIG_RF5C68_DAC_PERIOD, 1, 1);
73                 break;
74         }
75 }
76         
77 uint32_t ADPCM::read_io8(uint32_t addr)
78 {
79         /*
80           0x04e7 - 0x04e8 : ADC
81           0x04f0 - 0x04f8 : DAC
82         */
83         uint8_t val = 0xff;
84         switch(addr & 0x1f) {
85         case 0x07: // ADC data register
86                 if(!(adc_fifo->empty())) {
87                         val = (uint8_t)(adc_fifo->read() & 0xff);
88                 } else {
89                         val = 0x00;
90                 }
91                 break;
92         case 0x08: // ADC flags
93                 val = (!(adc_fifo->empty())) ? 0x01 : 0x00;
94                 break;
95         case 0x09: // Int13 reason
96                 {
97                         bool intr_pcm = false;
98                         for(int i = 0; i < 8; i++) {
99                                 if(dac_intr[i]) {
100                                         intr_pcm = true;
101                                         break;
102                                 }
103                         }
104                         val = 0xf6 | ((intr_pcm) ? 0x08 : 0x00) | ((intr_opx) ? 0x01 : 0x00);
105                 }
106                 break;
107         case 0x0a: // PCM Interrupt mask
108                 val = 0x00;
109                 for(int i = 0; i < 8; i++) {
110                         val = val | ((dac_int_mask[i]) ? (0x01 << i) : 0);
111                 }
112                 break;
113         case 0x0b: // PCM Interrupt status
114                 {
115                         bool _s = false;
116                         val = 0x00;
117                         for(int i = 0; i < 8; i++) {
118                                 val = val | ((dac_intr[i]) ? (0x01 << i) : 0);
119                         }
120                         for(int i = 0; i < 8; i++) {
121                                 if(dac_intr[i]) {
122                                         _s = true;
123                                 }
124                                 dac_intr[i] = false;
125                         }
126                         if(_s) {
127                                 d_pic->write_signal(SIG_I8259_IR5 | SIG_I8259_CHIP1, 0x00000000, 0xffffffff);
128                         }
129                 }
130                 break;
131         default:
132                 if((addr & 0x1f) >= 0x10) val = d_rf5c68->read_io8(addr & 0x0f); // AROUND DAC
133                 break;
134         }
135         return val;
136 }
137
138 void ADPCM::write_io8(uint32_t addr, uint32_t data)
139 {
140         uint32_t naddr = addr & 0x1f;
141         if(naddr == 0x08) {
142                 adc_fifo->clear();
143         } else if(naddr == 0x0a) {
144                 uint32_t mask = 0x01;
145                 for(int i = 0; i < 8; i++) {
146                         if((data & mask) != 0) {
147                                 dac_int_mask[i] = true;
148                         } else {
149                                 dac_int_mask[i] = false;
150                         }
151                         mask <<= 1;
152                 }
153         } else if(naddr >= 0x10) {
154                 d_rf5c68->write_io8(naddr & 0x0f, data);
155         }
156 }
157
158 uint32_t ADPCM::read_data8(uint32_t addr)
159 {
160         if((addr >= 0xc2200000) && (addr < 0xc2201000)) {
161                 return d_rf5c68->read_data8(addr & 0x0fff);
162         }
163         return 0xff;
164 }
165
166 void ADPCM::write_data8(uint32_t addr, uint32_t data)
167 {
168         if((addr >= 0xc2200000) && (addr < 0xc2201000)) {
169                 d_rf5c68->write_data8(addr & 0x0fff, data);
170         }
171 }
172
173 void ADPCM::write_signal(int ch, uint32_t data, uint32_t mask)
174 {
175         if(ch == SIG_ADPCM_WRITE_INTERRUPT) {
176                 uint32_t n_ch = data & 0x07;
177                 bool n_onoff = (((data & mask) & 0x00000008) != 0) ? true : false;
178                 bool n_allset =(((data & mask) & 0x80000000) != 0) ? true : false;
179                 if(!(n_allset)) {
180                         n_onoff = n_onoff & dac_int_mask[n_ch];
181                         if(n_onoff != dac_intr[n_ch]) { // SET/RESET INT13                              
182                                 write_signals(&output_intr, (n_onoff) ? 0xffffffff : 0x00000000);
183                         }
184                         dac_intr[n_ch] = n_onoff;
185                 } else {
186                         // ALLSET
187                         bool n_backup;
188                         bool _s = false;
189                         for(int i = 0; i < 8; i++) { // SET/RESET INT13                         
190                                 n_backup = dac_intr[i];
191                                 dac_intr[i] = n_onoff & dac_int_mask[i];
192                                 if(n_backup != dac_intr[i]) _s = true;
193                         }
194                         if(_s) {
195                                 write_signals(&output_intr, (n_onoff) ? 0xffffffff : 0x00000000);
196                         }
197                 }
198         } else if(ch == SIG_ADPCM_OPX_INTR) { // SET/RESET INT13
199                 intr_opx = ((data & mask) != 0); 
200                 write_signals(&output_intr, (intr_opx) ? 0xffffffff : 0x00000000);
201         } else if(ch == SIG_ADPCM_ADC_INTR) { // Push data to FIFO from ADC.
202                 if((data & mask) != 0) {
203                         uint32_t n_data = d_adc->read_signal(SIG_AD7820_DATA_REG);
204                         d_adc->write_signal(SIG_AD7820_CS, 0, 0xffffffff);
205                         if(!(adc_fifo->full())) {
206                                 adc_fifo->write((int)(n_data & 0xff));
207                         }
208                 }
209         }
210 }
211
212 #define STATE_VERSION   1
213
214 bool ADPCM::process_state(FILEIO* state_fio, bool loading)
215 {
216         if(!state_fio->StateCheckUint32(STATE_VERSION)) {
217                 return false;
218         }
219         if(!state_fio->StateCheckInt32(this_device_id)) {
220                 return false;
221         }
222         if(!(adc_fifo->process_state((void *)state_fio, loading))) {
223                 return false;
224         }
225         state_fio->StateValue(intr_opx);
226         state_fio->StateArray(dac_intr,     sizeof(dac_intr), 1);
227         state_fio->StateArray(dac_int_mask, sizeof(dac_int_mask), 1);
228         return true;
229 }
230         
231 }