2 Skelton for retropc emulator
4 Author : Takeda.Toshiya
12 #define EVENT_COUNTER 0
15 void Z80CTC::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"));
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;
36 counter[ch].req_intr = false;
37 counter[ch].in_service = false;
38 counter[ch].vector = ch << 1;
43 void Z80CTC::write_io8(uint32_t addr, uint32_t data)
46 if(counter[ch].latch) {
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;
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;
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;
81 uint32_t Z80CTC::read_io8(uint32_t addr)
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;
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);
100 } else if(counter[ch].sysclock_id != -1) {
101 int passed = get_passed_clock(counter[ch].prev);
103 //#ifdef Z80CTC_CLOCKS
104 if(_E_Z80CTC_CLOCKS) {
105 input = (uint32_t)(passed * __Z80CTC_CLOCKS / cpu_clocks);
111 if(counter[ch].input <= input) {
112 input = counter[ch].input - 1;
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);
124 return counter[ch].count & 0xff;
127 void Z80CTC::event_callback(int event_id, int err)
129 int ch = event_id & 3;
131 input_sysclock(ch, counter[ch].input);
132 counter[ch].sysclock_id = -1;
134 input_clock(ch, counter[ch].input);
135 counter[ch].clock_id = -1;
137 update_event(ch, err);
140 void Z80CTC::write_signal(int id, uint32_t data, uint32_t mask)
149 // more correct implements...
150 bool next = ((data & mask) != 0);
151 if(counter[ch].prev_in != next) {
152 if(counter[ch].slope == next) {
156 counter[ch].prev_in = next;
161 void Z80CTC::input_clock(int ch, int clock)
163 if(!(counter[ch].control & 0x40)) {
164 // now timer mode, start timer and quit !!!
165 counter[ch].start = true;
168 if(counter[ch].freeze) {
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;
180 write_signals(&counter[ch].outputs, 0xffffffff);
181 write_signals(&counter[ch].outputs, 0);
185 void Z80CTC::input_sysclock(int ch, int clock)
187 if(counter[ch].control & 0x40) {
188 // now counter mode, quit !!!
191 if(!counter[ch].start || counter[ch].freeze) {
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;
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;
206 write_signals(&counter[ch].outputs, 0xffffffff);
207 write_signals(&counter[ch].outputs, 0);
211 void Z80CTC::update_event(int ch, int err)
213 if(counter[ch].control & 0x40) {
215 if(counter[ch].sysclock_id != -1) {
216 cancel_event(this, counter[ch].sysclock_id);
218 counter[ch].sysclock_id = -1;
220 if(counter[ch].freeze) {
221 if(counter[ch].clock_id != -1) {
222 cancel_event(this, counter[ch].clock_id);
224 counter[ch].clock_id = -1;
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);
235 if(counter[ch].clock_id != -1) {
236 cancel_event(this, counter[ch].clock_id);
238 counter[ch].clock_id = -1;
240 if(!counter[ch].start || counter[ch].freeze) {
241 if(counter[ch].sysclock_id != -1) {
242 cancel_event(this, counter[ch].sysclock_id);
244 counter[ch].sysclock_id = -1;
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;
254 counter[ch].period = counter[ch].input + err;
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);
263 void Z80CTC::set_intr_iei(bool val)
271 #define set_intr_oei(val) { \
275 d_child->set_intr_iei(oei); \
280 void Z80CTC::update_intr()
285 if((next = iei) == true) {
286 for(int ch = 0; ch < 4; ch++) {
287 if(counter[ch].in_service) {
296 if((next = iei) == true) {
298 for(int ch = 0; ch < 4; ch++) {
299 if(counter[ch].in_service) {
302 if(counter[ch].req_intr) {
309 d_cpu->set_intr_line(next, true, intr_bit);
313 uint32_t Z80CTC::get_intr_ack()
316 for(int ch = 0; ch < 4; ch++) {
317 if(counter[ch].in_service) {
318 // invalid interrupt status
320 } else if(counter[ch].req_intr) {
321 counter[ch].req_intr = false;
322 counter[ch].in_service = true;
324 return counter[ch].vector;
328 return d_child->get_intr_ack();
333 void Z80CTC::notify_intr_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; // ???
345 d_child->notify_intr_reti();
349 #define STATE_VERSION 1
351 bool Z80CTC::process_state(FILEIO* state_fio, bool loading)
353 if(!state_fio->StateCheckUint32(STATE_VERSION)) {
356 if(!state_fio->StateCheckInt32(this_device_id)) {
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);
381 state_fio->StateValue(cpu_clocks);
382 state_fio->StateValue(iei);
383 state_fio->StateValue(oei);
384 state_fio->StateValue(intr_bit);