OSDN Git Service

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