OSDN Git Service

[VM][FMTOWNS] Add FONT ROMS, MSDOS ROM, SYSTEM ROM and SERIAL ROM.
[csp-qt/common_source_project-fm7.git] / source / src / vm / z80ctc.cpp
1 /*
2         Skelton for retropc emulator
3
4         Author : Takeda.Toshiya
5         Date   : 2006.08.18 -
6
7         [ Z80CTC ]
8 */
9
10 #include "z80ctc.h"
11
12 #define EVENT_COUNTER   0
13 #define EVENT_TIMER     4
14
15 void Z80CTC::initialize()
16 {
17         DEVICE::initialize();
18         _E_Z80CTC_CLOCKS = osd->check_feature(_T("Z80CTC_CLOCKS"));
19         if(_E_Z80CTC_CLOCKS) {
20                 __Z80CTC_CLOCKS = osd->get_feature_double_value(_T("Z80CTC_CLOCKS"));
21         }
22 }
23
24 void Z80CTC::reset()
25 {
26         for(int ch = 0; ch < 4; ch++) {
27                 counter[ch].count = counter[ch].constant = 256;
28                 counter[ch].clocks = 0;
29                 counter[ch].control = 0;
30                 counter[ch].slope = false;
31                 counter[ch].prescaler = 256;
32                 counter[ch].freeze = counter[ch].start = counter[ch].latch = false;
33                 counter[ch].clock_id = counter[ch].sysclock_id = -1;
34                 counter[ch].first_constant = true;
35                 // interrupt
36                 counter[ch].req_intr = false;
37                 counter[ch].in_service = false;
38                 counter[ch].vector = ch << 1;
39         }
40         iei = oei = true;
41 }
42
43 void Z80CTC::write_io8(uint32_t addr, uint32_t data)
44 {
45         int ch = addr & 3;
46         if(counter[ch].latch) {
47                 // time constant
48                 counter[ch].constant = data ? data : 256;
49                 counter[ch].latch = false;
50                 if(counter[ch].freeze || counter[ch].first_constant) {
51                         counter[ch].count = counter[ch].constant;
52                         counter[ch].clocks = 0;
53                         counter[ch].freeze = false;
54                         counter[ch].first_constant = false;
55                         update_event(ch, 0);
56                 }
57         } else {
58                 if(data & 1) {
59                         // control word
60                         counter[ch].prescaler = (data & 0x20) ? 256 : 16;
61                         counter[ch].latch = ((data & 4) != 0);
62                         counter[ch].freeze = ((data & 2) != 0);
63                         counter[ch].start = (counter[ch].freq || !(data & 8));
64                         counter[ch].control = data;
65                         counter[ch].slope = ((data & 0x10) != 0);
66                         if(!(data & 0x80) && counter[ch].req_intr) {
67                                 counter[ch].req_intr = false;
68                                 update_intr();
69                         }
70                         update_event(ch, 0);
71                 } else if(ch == 0) {
72                         // vector
73                         counter[0].vector = (data & 0xf8) | 0;
74                         counter[1].vector = (data & 0xf8) | 2;
75                         counter[2].vector = (data & 0xf8) | 4;
76                         counter[3].vector = (data & 0xf8) | 6;
77                 }
78         }
79 }
80
81 uint32_t Z80CTC::read_io8(uint32_t addr)
82 {
83         int ch = addr & 3;
84         // update counter
85         if(counter[ch].clock_id != -1) {
86                 int passed = get_passed_clock(counter[ch].prev);
87                 uint32_t input = (uint32_t)(counter[ch].freq * passed / cpu_clocks);
88                 if(counter[ch].input <= input) {
89                         input = counter[ch].input - 1;
90                 }
91                 if(input > 0) {
92                         input_clock(ch, input);
93                         // cancel and re-register event
94                         cancel_event(this, counter[ch].clock_id);
95                         counter[ch].input -= input;
96                         counter[ch].period -= passed;
97                         counter[ch].prev = get_current_clock();
98                         register_event_by_clock(this, EVENT_COUNTER + ch, counter[ch].period, false, &counter[ch].clock_id);
99                 }
100         } else if(counter[ch].sysclock_id != -1) {
101                 int passed = get_passed_clock(counter[ch].prev);
102                 uint32_t input;
103 //#ifdef Z80CTC_CLOCKS
104                 if(_E_Z80CTC_CLOCKS) {
105                         input = (uint32_t)(passed * __Z80CTC_CLOCKS / cpu_clocks);
106                 } else {                
107 //#else
108                         input = passed;
109                 }
110 //#endif
111                 if(counter[ch].input <= input) {
112                         input = counter[ch].input - 1;
113                 }
114                 if(input > 0) {
115                         input_sysclock(ch, input);
116                         // cancel and re-register event
117                         cancel_event(this, counter[ch].sysclock_id);
118                         counter[ch].input -= passed;
119                         counter[ch].period -= passed;
120                         counter[ch].prev = get_current_clock();
121                         register_event_by_clock(this, EVENT_TIMER + ch, counter[ch].period, false, &counter[ch].sysclock_id);
122                 }
123         }
124         return counter[ch].count & 0xff;
125 }
126
127 void Z80CTC::event_callback(int event_id, int err)
128 {
129         int ch = event_id & 3;
130         if(event_id & 4) {
131                 input_sysclock(ch, counter[ch].input);
132                 counter[ch].sysclock_id = -1;
133         } else {
134                 input_clock(ch, counter[ch].input);
135                 counter[ch].clock_id = -1;
136         }
137         update_event(ch, err);
138 }
139
140 void Z80CTC::write_signal(int id, uint32_t data, uint32_t mask)
141 {
142         int ch = id & 3;
143 #if 1
144         if(data & mask) {
145                 input_clock(ch, 1);
146                 update_event(ch, 0);
147         }
148 #else
149         // more correct implements...
150         bool next = ((data & mask) != 0);
151         if(counter[ch].prev_in != next) {
152                 if(counter[ch].slope == next) {
153                         input_clock(ch, 1);
154                         update_event(ch, 0);
155                 }
156                 counter[ch].prev_in = next;
157         }
158 #endif
159 }
160
161 void Z80CTC::input_clock(int ch, int clock)
162 {
163         if(!(counter[ch].control & 0x40)) {
164                 // now timer mode, start timer and quit !!!
165                 counter[ch].start = true;
166                 return;
167         }
168         if(counter[ch].freeze) {
169                 return;
170         }
171         
172         // update counter
173         counter[ch].count -= clock;
174         while(counter[ch].count <= 0) {
175                 counter[ch].count += counter[ch].constant;
176                 if(counter[ch].control & 0x80) {
177                         counter[ch].req_intr = true;
178                         update_intr();
179                 }
180                 write_signals(&counter[ch].outputs, 0xffffffff);
181                 write_signals(&counter[ch].outputs, 0);
182         }
183 }
184
185 void Z80CTC::input_sysclock(int ch, int clock)
186 {
187         if(counter[ch].control & 0x40) {
188                 // now counter mode, quit !!!
189                 return;
190         }
191         if(!counter[ch].start || counter[ch].freeze) {
192                 return;
193         }
194         counter[ch].clocks += clock;
195         int input = counter[ch].clocks >> (counter[ch].prescaler == 256 ? 8 : 4);
196         counter[ch].clocks &= counter[ch].prescaler - 1;
197         
198         // update counter
199         counter[ch].count -= input;
200         while(counter[ch].count <= 0) {
201                 counter[ch].count += counter[ch].constant;
202                 if(counter[ch].control & 0x80) {
203                         counter[ch].req_intr = true;
204                         update_intr();
205                 }
206                 write_signals(&counter[ch].outputs, 0xffffffff);
207                 write_signals(&counter[ch].outputs, 0);
208         }
209 }
210
211 void Z80CTC::update_event(int ch, int err)
212 {
213         if(counter[ch].control & 0x40) {
214                 // counter mode
215                 if(counter[ch].sysclock_id != -1) {
216                         cancel_event(this, counter[ch].sysclock_id);
217                 }
218                 counter[ch].sysclock_id = -1;
219                 
220                 if(counter[ch].freeze) {
221                         if(counter[ch].clock_id != -1) {
222                                 cancel_event(this, counter[ch].clock_id);
223                         }
224                         counter[ch].clock_id = -1;
225                         return;
226                 }
227                 if(counter[ch].clock_id == -1 && counter[ch].freq) {
228                         counter[ch].input = counter[ch].count;
229                         counter[ch].period = (uint32_t)(cpu_clocks * counter[ch].input / counter[ch].freq) + err;
230                         counter[ch].prev = get_current_clock() + err;
231                         register_event_by_clock(this, EVENT_COUNTER + ch, counter[ch].period, false, &counter[ch].clock_id);
232                 }
233         } else {
234                 // timer mode
235                 if(counter[ch].clock_id != -1) {
236                         cancel_event(this, counter[ch].clock_id);
237                 }
238                 counter[ch].clock_id = -1;
239                 
240                 if(!counter[ch].start || counter[ch].freeze) {
241                         if(counter[ch].sysclock_id != -1) {
242                                 cancel_event(this, counter[ch].sysclock_id);
243                         }
244                         counter[ch].sysclock_id = -1;
245                         return;
246                 }
247                 if(counter[ch].sysclock_id == -1) {
248                         counter[ch].input = counter[ch].count * counter[ch].prescaler - counter[ch].clocks;
249 //#ifdef Z80CTC_CLOCKS
250                         if(_E_Z80CTC_CLOCKS) {
251                                 counter[ch].period = (uint32_t)(counter[ch].input * cpu_clocks / __Z80CTC_CLOCKS) + err;
252                         } else {
253 //#else
254                                 counter[ch].period = counter[ch].input + err;
255                         }
256 //#endif
257                         counter[ch].prev = get_current_clock() + err;
258                         register_event_by_clock(this, EVENT_TIMER + ch, counter[ch].period, false, &counter[ch].sysclock_id);
259                 }
260         }
261 }
262
263 void Z80CTC::set_intr_iei(bool val)
264 {
265         if(iei != val) {
266                 iei = val;
267                 update_intr();
268         }
269 }
270
271 #define set_intr_oei(val) { \
272         if(oei != val) { \
273                 oei = val; \
274                 if(d_child) { \
275                         d_child->set_intr_iei(oei); \
276                 } \
277         } \
278 }
279
280 void Z80CTC::update_intr()
281 {
282         bool next;
283         
284         // set oei signal
285         if((next = iei) == true) {
286                 for(int ch = 0; ch < 4; ch++) {
287                         if(counter[ch].in_service) {
288                                 next = false;
289                                 break;
290                         }
291                 }
292         }
293         set_intr_oei(next);
294         
295         // set int signal
296         if((next = iei) == true) {
297                 next = false;
298                 for(int ch = 0; ch < 4; ch++) {
299                         if(counter[ch].in_service) {
300                                 break;
301                         }
302                         if(counter[ch].req_intr) {
303                                 next = true;
304                                 break;
305                         }
306                 }
307         }
308         if(d_cpu) {
309                 d_cpu->set_intr_line(next, true, intr_bit);
310         }
311 }
312
313 uint32_t Z80CTC::get_intr_ack()
314 {
315         // ack (M1=IORQ=L)
316         for(int ch = 0; ch < 4; ch++) {
317                 if(counter[ch].in_service) {
318                         // invalid interrupt status
319                         return 0xff;
320                 } else if(counter[ch].req_intr) {
321                         counter[ch].req_intr = false;
322                         counter[ch].in_service = true;
323                         update_intr();
324                         return counter[ch].vector;
325                 }
326         }
327         if(d_child) {
328                 return d_child->get_intr_ack();
329         }
330         return 0xff;
331 }
332
333 void Z80CTC::notify_intr_reti()
334 {
335         // detect RETI
336         for(int ch = 0; ch < 4; ch++) {
337                 if(counter[ch].in_service) {
338                         counter[ch].in_service = false;
339                         counter[ch].req_intr = false; // ???
340                         update_intr();
341                         return;
342                 }
343         }
344         if(d_child) {
345                 d_child->notify_intr_reti();
346         }
347 }
348
349 #define STATE_VERSION   1
350
351 bool Z80CTC::process_state(FILEIO* state_fio, bool loading)
352 {
353         if(!state_fio->StateCheckUint32(STATE_VERSION)) {
354                 return false;
355         }
356         if(!state_fio->StateCheckInt32(this_device_id)) {
357                 return false;
358         }
359         for(int i = 0; i < 4; i++) {
360                 state_fio->StateValue(counter[i].control);
361                 state_fio->StateValue(counter[i].slope);
362                 state_fio->StateValue(counter[i].count);
363                 state_fio->StateValue(counter[i].constant);
364                 state_fio->StateValue(counter[i].vector);
365                 state_fio->StateValue(counter[i].clocks);
366                 state_fio->StateValue(counter[i].prescaler);
367                 state_fio->StateValue(counter[i].freeze);
368                 state_fio->StateValue(counter[i].start);
369                 state_fio->StateValue(counter[i].latch);
370                 state_fio->StateValue(counter[i].prev_in);
371                 state_fio->StateValue(counter[i].first_constant);
372                 state_fio->StateValue(counter[i].freq);
373                 state_fio->StateValue(counter[i].clock_id);
374                 state_fio->StateValue(counter[i].sysclock_id);
375                 state_fio->StateValue(counter[i].input);
376                 state_fio->StateValue(counter[i].period);
377                 state_fio->StateValue(counter[i].prev);
378                 state_fio->StateValue(counter[i].req_intr);
379                 state_fio->StateValue(counter[i].in_service);
380         }
381         state_fio->StateValue(cpu_clocks);
382         state_fio->StateValue(iei);
383         state_fio->StateValue(oei);
384         state_fio->StateValue(intr_bit);
385         return true;
386 }