1 /*
2 * Copyright (C) 2002-2021 The DOSBox Team
3 *
4 * This program is free software; you can redistribute it and/or modify
5 * it under the terms of the GNU General Public License as published by
6 * the Free Software Foundation; either version 2 of the License, or
7 * (at your option) any later version.
8 *
9 * This program is distributed in the hope that it will be useful,
10 * but WITHOUT ANY WARRANTY; without even the implied warranty of
11 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
12 * GNU General Public License for more details.
13 *
14 * You should have received a copy of the GNU General Public License along
15 * with this program; if not, write to the Free Software Foundation, Inc.,
16 * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
17 */
18
19
20 #include <math.h>
21 #include "dosbox.h"
22 #include "inout.h"
23 #include "pic.h"
24 #include "mem.h"
25 #include "mixer.h"
26 #include "timer.h"
27 #include "setup.h"
28 #include "support.h"
29
30 const std::chrono::steady_clock::time_point system_start_time = std::chrono::steady_clock::now();
31
BIN2BCD(Bit16u & val)32 static inline void BIN2BCD(Bit16u& val) {
33 const auto b = ((val / 10) % 10) << 4;
34 const auto c = ((val / 100) % 10) << 8;
35 const auto d = ((val / 1000) % 10) << 12;
36 assert(b + c + d <= UINT16_MAX);
37
38 const uint16_t temp = (val % 10) + static_cast<uint16_t>(b + c + d);
39 val = temp;
40 }
41
BCD2BIN(Bit16u & val)42 static inline void BCD2BIN(Bit16u& val) {
43 Bit16u temp= (val&0x0f) +((val>>4)&0x0f) *10 +((val>>8)&0x0f) *100 +((val>>12)&0x0f) *1000;
44 val=temp;
45 }
46
47 struct PIT_Block {
48 uint32_t cntr;
49 double delay;
50 double start;
51
52 Bit16u read_latch;
53 Bit16u write_latch;
54
55 Bit8u mode;
56 Bit8u latch_mode;
57 Bit8u read_state;
58 Bit8u write_state;
59
60 bool bcd;
61 bool go_read_latch;
62 bool new_mode;
63 bool counterstatus_set;
64 bool counting;
65 bool update_count;
66 };
67
68 static PIT_Block pit[3];
69 static bool gate2;
70
71 static Bit8u latched_timerstatus;
72 // the timer status can not be overwritten until it is read or the timer was
73 // reprogrammed.
74 static bool latched_timerstatus_locked;
75
PIT0_Event(uint32_t)76 static void PIT0_Event(uint32_t /*val*/)
77 {
78 PIC_ActivateIRQ(0);
79 if (pit[0].mode != 0) {
80 pit[0].start += pit[0].delay;
81
82 if (GCC_UNLIKELY(pit[0].update_count)) {
83 pit[0].delay = (1000.0 / (static_cast<double>(PIT_TICK_RATE) /
84 (double)pit[0].cntr));
85 pit[0].update_count = false;
86 }
87 PIC_AddEvent(PIT0_Event,pit[0].delay);
88 }
89 }
90
counter_output(const uint32_t counter)91 static bool counter_output(const uint32_t counter)
92 {
93 PIT_Block *p = &pit[counter];
94 auto index = PIC_FullIndex() - p->start;
95 switch (p->mode) {
96 case 0:
97 if (p->new_mode) return false;
98 if (index>p->delay) return true;
99 else return false;
100 break;
101 case 2:
102 if (p->new_mode) return true;
103 index = fmod(index, p->delay);
104 return index>0;
105 case 3:
106 if (p->new_mode) return true;
107 index = fmod(index, p->delay);
108 return index*2<p->delay;
109 case 4:
110 //Only low on terminal count
111 // if(fmod(index,(double)p->delay) == 0) return false; //Maybe take one rate tick in consideration
112 //Easiest solution is to report always high (Space marines uses this mode)
113 return true;
114 default:
115 LOG(LOG_PIT,LOG_ERROR)("Illegal Mode %d for reading output",p->mode);
116 return true;
117 }
118 }
status_latch(const uint32_t counter)119 static void status_latch(const uint32_t counter)
120 {
121 // the timer status can not be overwritten until it is read or the timer
122 // was reprogrammed.
123 if (!latched_timerstatus_locked) {
124 PIT_Block *p = &pit[counter];
125 latched_timerstatus = 0;
126 // Timer Status Word
127 // 0: BCD
128 // 1-3: Timer mode
129 // 4-5: read/load mode
130 // 6: "NULL" - this is 0 if "the counter value is in the
131 // counter" ;) should rarely be 1 (i.e. on exotic modes) 7: OUT
132 // - the logic level on the Timer output pin
133 if (p->bcd)
134 latched_timerstatus |= 0x1;
135 latched_timerstatus |= ((p->mode & 7) << 1);
136 if ((p->read_state == 0) || (p->read_state == 3))
137 latched_timerstatus |= 0x30;
138 else if (p->read_state == 1)
139 latched_timerstatus |= 0x10;
140 else if (p->read_state == 2)
141 latched_timerstatus |= 0x20;
142 if (counter_output(counter))
143 latched_timerstatus |= 0x80;
144 if (p->new_mode)
145 latched_timerstatus |= 0x40;
146 // The first thing that is being read from this counter now is
147 // the counter status.
148 p->counterstatus_set = true;
149 latched_timerstatus_locked = true;
150 }
151 }
counter_latch(uint32_t counter)152 static void counter_latch(uint32_t counter)
153 {
154 /* Fill the read_latch of the selected counter with current count */
155 PIT_Block * p=&pit[counter];
156 p->go_read_latch=false;
157
158 //If gate2 is disabled don't update the read_latch
159 if (counter == 2 && !gate2 && p->mode !=1) return;
160
161 auto elapsed_ms = PIC_FullIndex() - p->start;
162 auto save_read_latch = [p](double latch_time) {
163 // Latch is a 16-bit counter, so ensure it doesn't overflow
164 const auto bound_latch = clamp(static_cast<int>(latch_time), 0,
165 static_cast<int>(UINT16_MAX));
166 p->read_latch = static_cast<uint16_t>(bound_latch);
167 };
168
169 if (GCC_UNLIKELY(p->new_mode)) {
170 const auto total_ticks = static_cast<uint32_t>(elapsed_ms /
171 PERIOD_OF_1K_PIT_TICKS);
172 // if (p->mode==3) ticks_since_then /= 2; // TODO figure this
173 // out on real hardware
174 save_read_latch(p->read_latch - total_ticks);
175 return;
176 }
177 const auto cntr = static_cast<double>(p->cntr);
178 switch (p->mode) {
179 case 4: /* Software Triggered Strobe */
180 case 0: /* Interrupt on Terminal Count */
181 /* Counter keeps on counting after passing terminal count */
182 if (elapsed_ms > p->delay) {
183 elapsed_ms -= p->delay;
184 if (p->bcd) {
185 elapsed_ms = fmod(elapsed_ms, PERIOD_OF_1K_PIT_TICKS * 10000.0);
186 save_read_latch(9999 - elapsed_ms * PIT_TICK_RATE_KHZ);
187 } else {
188 elapsed_ms = fmod(elapsed_ms, PERIOD_OF_1K_PIT_TICKS * 0x10000);
189 save_read_latch(0xffff - elapsed_ms * PIT_TICK_RATE_KHZ);
190 }
191 } else {
192 save_read_latch(cntr - elapsed_ms * PIT_TICK_RATE_KHZ);
193 }
194 break;
195 case 1: // countdown
196 if(p->counting) {
197 if (elapsed_ms > p->delay) { // has timed out
198 save_read_latch(0xffff); // unconfirmed
199 } else {
200 save_read_latch(cntr - elapsed_ms * PIT_TICK_RATE_KHZ);
201 }
202 }
203 break;
204 case 2: /* Rate Generator */
205 elapsed_ms = fmod(elapsed_ms, p->delay);
206 save_read_latch(cntr - (elapsed_ms / p->delay) * cntr);
207 break;
208 case 3: /* Square Wave Rate Generator */
209 elapsed_ms = fmod(elapsed_ms, p->delay);
210 elapsed_ms *= 2;
211 if (elapsed_ms > p->delay)
212 elapsed_ms -= p->delay;
213 save_read_latch(cntr - (elapsed_ms / p->delay) * cntr);
214 // In mode 3 it never returns odd numbers LSB (if odd number is
215 // written 1 will be subtracted on first clock and then always
216 // 2) fixes "Corncob 3D"
217 save_read_latch(p->read_latch & 0xfffe);
218 break;
219 default:
220 LOG(LOG_PIT,LOG_ERROR)("Illegal Mode %d for reading counter %d",p->mode,counter);
221 save_read_latch(0xffff);
222 break;
223 }
224 }
225
write_latch(io_port_t port,io_val_t value,io_width_t)226 static void write_latch(io_port_t port, io_val_t value, io_width_t)
227 {
228 const auto val = check_cast<uint8_t>(value);
229 // LOG(LOG_PIT,LOG_ERROR)("port %X write:%X
230 // state:%X",port,val,pit[port-0x40].write_state);
231 const uint16_t counter = port - 0x40;
232 PIT_Block *p = &pit[counter];
233 if (p->bcd == true)
234 BIN2BCD(p->write_latch);
235
236 switch (p->write_state) {
237 case 0:
238 // write_latch is 16-bits
239 p->write_latch = static_cast<uint16_t>(p->write_latch |
240 ((val & 0xff) << 8));
241 p->write_state = 3;
242 break;
243 case 3:
244 p->write_latch = val & 0xff;
245 p->write_state = 0;
246 break;
247 case 1: p->write_latch = val & 0xff; break;
248 case 2:
249 p->write_latch = static_cast<uint16_t>((val & 0xff) << 8);
250 break;
251 }
252
253 if (p->bcd == true)
254 BCD2BIN(p->write_latch);
255
256 if (p->write_state != 0) {
257 if (p->write_latch == 0) {
258 if (p->bcd == false)
259 p->cntr = 0x10000;
260 else
261 p->cntr = 9999;
262 }
263 // square wave, count by 2
264 else if (p->write_latch == 1 && p->mode == 3)
265 // counter==1 and mode==3 makes a low frequency
266 // buzz (Paratrooper)
267 p->cntr = p->bcd ? 10000 : 0x10001;
268 else
269 p->cntr = p->write_latch;
270
271 if ((!p->new_mode) && (p->mode == 2) && (counter == 0)) {
272 // In mode 2 writing another value has no direct
273 // effect on the count until the old one has run
274 // out. This might apply to other modes too.
275 // This is not fixed for PIT2 yet!!
276 p->update_count = true;
277 return;
278 }
279 p->start = PIC_FullIndex();
280 p->delay = (1000.0 / ((double)PIT_TICK_RATE / (double)p->cntr));
281
282 switch (counter) {
283 case 0x00: /* Timer hooked to IRQ 0 */
284 if (p->new_mode || p->mode == 0) {
285 if (p->mode == 0) { // DoWhackaDo demo
286 PIC_RemoveEvents(PIT0_Event);
287 }
288 PIC_AddEvent(PIT0_Event, p->delay);
289 } else
290 LOG(LOG_PIT, LOG_NORMAL)
291 ("PIT 0 Timer set without new control word");
292 LOG(LOG_PIT, LOG_NORMAL)
293 ("PIT 0 Timer at %.4f Hz mode %d", 1000.0 / p->delay,
294 p->mode);
295 break;
296 case 0x02: // Timer hooked to PC-Speaker
297 // LOG(LOG_PIT,"PIT 2 Timer at %.3g Hz mode %d",
298 // PIT_TICK_RATE/(double)p->cntr,p->mode);
299 PCSPEAKER_SetCounter(p->cntr, p->mode);
300 break;
301 default:
302 LOG(LOG_PIT, LOG_ERROR)
303 ("PIT:Illegal timer selected for writing");
304 }
305 p->new_mode = false;
306 }
307 }
308
read_latch(io_port_t port,io_width_t)309 static uint8_t read_latch(io_port_t port, io_width_t)
310 {
311 // LOG(LOG_PIT,LOG_ERROR)("port read %X",port);
312 const uint16_t counter = port - 0x40;
313 Bit8u ret = 0;
314 if (GCC_UNLIKELY(pit[counter].counterstatus_set)) {
315 pit[counter].counterstatus_set = false;
316 latched_timerstatus_locked = false;
317 ret = latched_timerstatus;
318 } else {
319 if (pit[counter].go_read_latch == true)
320 counter_latch(counter);
321
322 if( pit[counter].bcd == true) BIN2BCD(pit[counter].read_latch);
323
324 switch (pit[counter].read_state) {
325 case 0: /* read MSB & return to state 3 */
326 ret=(pit[counter].read_latch >> 8) & 0xff;
327 pit[counter].read_state = 3;
328 pit[counter].go_read_latch = true;
329 break;
330 case 3: /* read LSB followed by MSB */
331 ret = pit[counter].read_latch & 0xff;
332 pit[counter].read_state = 0;
333 break;
334 case 1: /* read LSB */
335 ret = pit[counter].read_latch & 0xff;
336 pit[counter].go_read_latch = true;
337 break;
338 case 2: /* read MSB */
339 ret = (pit[counter].read_latch >> 8) & 0xff;
340 pit[counter].go_read_latch = true;
341 break;
342 default:
343 E_Exit("Timer.cpp: error in readlatch");
344 break;
345 }
346 if( pit[counter].bcd == true) BCD2BIN(pit[counter].read_latch);
347 }
348 return ret;
349 }
350
write_p43(io_port_t,io_val_t value,io_width_t)351 static void write_p43(io_port_t, io_val_t value, io_width_t)
352 {
353 const auto val = check_cast<uint8_t>(value);
354 // LOG(LOG_PIT,LOG_ERROR)("port 43 %X",val);
355 const uint8_t latch = (val >> 6) & 0x03;
356 switch (latch) {
357 case 0:
358 case 1:
359 case 2:
360 if ((val & 0x30) == 0) {
361 /* Counter latch command */
362 counter_latch(latch);
363 } else {
364 // save output status to be used with timer 0 irq
365 bool old_output = counter_output(0);
366 // save the current count value to be re-used in
367 // undocumented newmode
368 counter_latch(latch);
369 pit[latch].bcd = (val & 1) > 0;
370 if (val & 1) {
371 if (pit[latch].cntr >= 9999)
372 pit[latch].cntr = 9999;
373 }
374
375 // Timer is being reprogrammed, unlock the status
376 if (pit[latch].counterstatus_set) {
377 pit[latch].counterstatus_set = false;
378 latched_timerstatus_locked = false;
379 }
380 pit[latch].start = PIC_FullIndex(); // for undocumented
381 // newmode
382 pit[latch].go_read_latch = true;
383 pit[latch].update_count = false;
384 pit[latch].counting = false;
385 pit[latch].read_state = (val >> 4) & 0x03;
386 pit[latch].write_state = (val >> 4) & 0x03;
387 Bit8u mode = (val >> 1) & 0x07;
388 if (mode > 5)
389 mode -= 4; // 6,7 become 2 and 3
390
391 pit[latch].mode = mode;
392
393 /* If the line goes from low to up => generate irq.
394 * ( BUT needs to stay up until acknowlegded by the
395 * cpu!!! therefore: ) If the line goes to low =>
396 * disable irq. Mode 0 starts with a low line. (so
397 * always disable irq) Mode 2,3 start with a high line.
398 * counter_output tells if the current counter is high
399 * or low So actually a mode 3 timer enables and
400 * disables irq al the time. (not handled) */
401
402 if (latch == 0) {
403 PIC_RemoveEvents(PIT0_Event);
404 if ((mode != 0) && !old_output) {
405 PIC_ActivateIRQ(0);
406 } else {
407 PIC_DeActivateIRQ(0);
408 }
409 } else if (latch == 2) {
410 PCSPEAKER_SetCounter(0, 3);
411 }
412 pit[latch].new_mode = true;
413 }
414 break;
415 case 3:
416 if ((val & 0x20) == 0) { /* Latch multiple pit counters */
417 if (val & 0x02)
418 counter_latch(0);
419 if (val & 0x04)
420 counter_latch(1);
421 if (val & 0x08)
422 counter_latch(2);
423 }
424 // status and values can be latched simultaneously
425 if ((val & 0x10) == 0) { /* Latch status words */
426 // but only 1 status can be latched simultaneously
427 if (val & 0x02)
428 status_latch(0);
429 else if (val & 0x04)
430 status_latch(1);
431 else if (val & 0x08)
432 status_latch(2);
433 }
434 break;
435 }
436 }
437
TIMER_SetGate2(bool in)438 void TIMER_SetGate2(bool in) {
439 //No changes if gate doesn't change
440 if(gate2 == in) return;
441 Bit8u & mode=pit[2].mode;
442 switch (mode) {
443 case 0:
444 if(in) pit[2].start = PIC_FullIndex();
445 else {
446 //Fill readlatch and store it.
447 counter_latch(2);
448 pit[2].cntr = pit[2].read_latch;
449 }
450 break;
451 case 1:
452 // gate 1 on: reload counter; off: nothing
453 if(in) {
454 pit[2].counting = true;
455 pit[2].start = PIC_FullIndex();
456 }
457 break;
458 case 2:
459 case 3:
460 //If gate is enabled restart counting. If disable store the current read_latch
461 if(in) pit[2].start = PIC_FullIndex();
462 else counter_latch(2);
463 break;
464 case 4:
465 case 5:
466 LOG(LOG_MISC,LOG_WARN)("unsupported gate 2 mode %x",mode);
467 break;
468 }
469 gate2 = in; //Set it here so the counter_latch above works
470 }
471
TIMER_GetOutput2(void)472 bool TIMER_GetOutput2(void) {
473 return counter_output(2);
474 }
475
476 class TIMER final : public Module_base{
477 private:
478 IO_ReadHandleObject ReadHandler[4];
479 IO_WriteHandleObject WriteHandler[4];
480 public:
TIMER(Section * configuration)481 TIMER(Section* configuration):Module_base(configuration){
482 WriteHandler[0].Install(0x40, write_latch, io_width_t::byte);
483 // WriteHandler[1].Install(0x41,write_latch,io_width_t::byte);
484 WriteHandler[2].Install(0x42, write_latch, io_width_t::byte);
485 WriteHandler[3].Install(0x43, write_p43, io_width_t::byte);
486 ReadHandler[0].Install(0x40, read_latch, io_width_t::byte);
487 ReadHandler[1].Install(0x41, read_latch, io_width_t::byte);
488 ReadHandler[2].Install(0x42, read_latch, io_width_t::byte);
489 /* Setup Timer 0 */
490 pit[0].cntr=0x10000;
491 pit[0].write_state = 3;
492 pit[0].read_state = 3;
493 pit[0].read_latch=0;
494 pit[0].write_latch=0;
495 pit[0].mode=3;
496 pit[0].bcd = false;
497 pit[0].go_read_latch = true;
498 pit[0].counterstatus_set = false;
499 pit[0].update_count = false;
500
501 pit[1].bcd = false;
502 pit[1].read_state = 1;
503 pit[1].go_read_latch = true;
504 pit[1].cntr = 18;
505 pit[1].mode = 2;
506 pit[1].write_state = 3;
507 pit[1].counterstatus_set = false;
508
509 pit[2].read_latch=1320; /* MadTv1 */
510 pit[2].write_state = 3; /* Chuck Yeager */
511 pit[2].read_state = 3;
512 pit[2].mode=3;
513 pit[2].bcd=false;
514 pit[2].cntr=1320;
515 pit[2].go_read_latch=true;
516 pit[2].counterstatus_set = false;
517 pit[2].counting = false;
518
519 pit[0].delay = (1000.0 / ((double)PIT_TICK_RATE / (double)pit[0].cntr));
520 pit[1].delay = (1000.0 / ((double)PIT_TICK_RATE / (double)pit[1].cntr));
521 pit[2].delay = (1000.0 / ((double)PIT_TICK_RATE / (double)pit[2].cntr));
522
523 latched_timerstatus_locked=false;
524 gate2 = false;
525 PIC_AddEvent(PIT0_Event,pit[0].delay);
526 }
~TIMER()527 ~TIMER(){
528 PIC_RemoveEvents(PIT0_Event);
529 }
530 };
531 static TIMER* test;
532
TIMER_Destroy(Section *)533 void TIMER_Destroy(Section*){
534 delete test;
535 }
TIMER_Init(Section * sec)536 void TIMER_Init(Section* sec) {
537 test = new TIMER(sec);
538 sec->AddDestroyFunction(&TIMER_Destroy);
539 }
540