2009-05-02 23:03:37 +02:00
|
|
|
/*
|
|
|
|
* Copyright (C) 2002 The DOSBox Team
|
|
|
|
*
|
|
|
|
* This program is free software; you can redistribute it and/or modify
|
|
|
|
* it under the terms of the GNU General Public License as published by
|
|
|
|
* the Free Software Foundation; either version 2 of the License, or
|
|
|
|
* (at your option) any later version.
|
|
|
|
*
|
|
|
|
* This program is distributed in the hope that it will be useful,
|
|
|
|
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
|
|
|
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
|
|
|
* GNU Library General Public License for more details.
|
|
|
|
*
|
|
|
|
* You should have received a copy of the GNU General Public License
|
|
|
|
* along with this program; if not, write to the Free Software
|
|
|
|
* Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
|
|
|
|
*/
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
#include <list>
|
|
|
|
#include "dosbox.h"
|
|
|
|
#include "inout.h"
|
|
|
|
#include "pic.h"
|
|
|
|
#include "bios.h"
|
|
|
|
#include "mem.h"
|
|
|
|
#include "dosbox.h"
|
|
|
|
#include "mixer.h"
|
|
|
|
#include "timer.h"
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
struct PIT_Block {
|
|
|
|
Bit8u mode; /* Current Counter Mode */
|
2009-05-02 23:27:47 +02:00
|
|
|
|
|
|
|
Bitu cntr;
|
|
|
|
Bits micro;
|
|
|
|
Bit64u start;
|
|
|
|
|
2009-05-02 23:03:37 +02:00
|
|
|
Bit8u latch_mode;
|
|
|
|
Bit8u read_state;
|
|
|
|
Bit16s read_latch;
|
|
|
|
Bit8u write_state;
|
|
|
|
Bit16u write_latch;
|
|
|
|
};
|
|
|
|
|
|
|
|
static PIT_Block pit[3];
|
|
|
|
|
2009-05-02 23:27:47 +02:00
|
|
|
static void PIT0_Event(void) {
|
|
|
|
PIC_ActivateIRQ(0);
|
|
|
|
PIC_AddEvent(PIT0_Event,pit[0].micro);
|
|
|
|
}
|
2009-05-02 23:03:37 +02:00
|
|
|
|
|
|
|
static void counter_latch(Bitu counter) {
|
|
|
|
/* Fill the read_latch of the selected counter with current count */
|
|
|
|
PIT_Block * p=&pit[counter];
|
2009-05-02 23:27:47 +02:00
|
|
|
|
|
|
|
Bit64s micro=PIC_MicroCount()-p->start;
|
2009-05-02 23:03:37 +02:00
|
|
|
switch (p->mode) {
|
2009-05-02 23:27:47 +02:00
|
|
|
case 0:
|
|
|
|
if (micro>p->micro) p->read_latch=p->write_latch;
|
|
|
|
else p->read_latch=(Bit16u)(p->cntr-(((float)micro/(float)p->micro)*(float)p->cntr));
|
|
|
|
break;
|
2009-05-02 23:03:37 +02:00
|
|
|
case 2:
|
2009-05-02 23:27:47 +02:00
|
|
|
micro%=p->micro;
|
|
|
|
p->read_latch=(Bit16u)(p->cntr-(((float)micro/(float)p->micro)*(float)p->cntr));
|
|
|
|
break;
|
2009-05-02 23:03:37 +02:00
|
|
|
case 3:
|
2009-05-02 23:27:47 +02:00
|
|
|
micro%=p->micro;
|
|
|
|
micro*=2;
|
|
|
|
if (micro>p->micro) micro-=p->micro;
|
|
|
|
p->read_latch=(Bit16u)(p->cntr-(((float)micro/(float)p->micro)*(float)p->cntr));
|
2009-05-02 23:03:37 +02:00
|
|
|
break;
|
|
|
|
default:
|
|
|
|
LOG_ERROR("PIT:Illegal Mode %d for reading counter %d",p->mode,counter);
|
2009-05-02 23:27:47 +02:00
|
|
|
micro%=p->micro;
|
|
|
|
p->read_latch=(Bit16u)(p->cntr-(((float)micro/(float)p->micro)*(float)p->cntr));
|
2009-05-02 23:03:37 +02:00
|
|
|
break;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
static void write_latch(Bit32u port,Bit8u val) {
|
2009-05-02 23:27:47 +02:00
|
|
|
Bitu counter=port-0x40;
|
2009-05-02 23:03:37 +02:00
|
|
|
PIT_Block * p=&pit[counter];
|
|
|
|
switch (p->write_state) {
|
|
|
|
case 0:
|
|
|
|
p->write_latch = p->write_latch | ((val & 0xff) << 8);
|
|
|
|
p->write_state = 3;
|
|
|
|
break;
|
|
|
|
case 3:
|
|
|
|
p->write_latch = val & 0xff;
|
|
|
|
p->write_state = 0;
|
|
|
|
break;
|
|
|
|
case 1:
|
|
|
|
p->write_latch = val & 0xff;
|
|
|
|
break;
|
|
|
|
case 2:
|
|
|
|
p->write_latch = (val & 0xff) << 8;
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
if (p->write_state != 0) {
|
|
|
|
if (p->write_latch == 0) p->cntr = 0x10000;
|
|
|
|
else p->cntr = p->write_latch;
|
2009-05-02 23:27:47 +02:00
|
|
|
p->start=PIC_MicroCount();
|
|
|
|
p->micro=1000000/((float)PIT_TICK_RATE/(float)p->cntr);
|
2009-05-02 23:03:37 +02:00
|
|
|
switch (counter) {
|
|
|
|
case 0x00: /* Timer hooked to IRQ 0 */
|
2009-05-02 23:27:47 +02:00
|
|
|
PIC_RemoveEvents(PIT0_Event);
|
|
|
|
PIC_AddEvent(PIT0_Event,p->micro);
|
2009-05-02 23:03:37 +02:00
|
|
|
LOG_DEBUG("PIT 0 Timer at %.3g Hz mode %d",PIT_TICK_RATE/(double)p->cntr,p->mode);
|
|
|
|
break;
|
|
|
|
case 0x02: /* Timer hooked to PC-Speaker */
|
2009-05-02 23:27:47 +02:00
|
|
|
// LOG_DEBUG("PIT 2 Timer at %.3g Hz mode %d",PIT_TICK_RATE/(double)p->cntr,p->mode);
|
2009-05-02 23:20:05 +02:00
|
|
|
PCSPEAKER_SetCounter(p->cntr,p->mode);
|
2009-05-02 23:03:37 +02:00
|
|
|
break;
|
|
|
|
default:
|
|
|
|
LOG_ERROR("PIT:Illegal timer selected for writing");
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
static Bit8u read_latch(Bit32u port) {
|
|
|
|
Bit32u counter=port-0x40;
|
|
|
|
if (pit[counter].read_latch == -1)
|
|
|
|
counter_latch(counter);
|
|
|
|
Bit8u ret;
|
|
|
|
switch (pit[counter].read_state) {
|
|
|
|
case 0: /* read MSB & return to state 3 */
|
|
|
|
ret=(pit[counter].read_latch >> 8) & 0xff;
|
|
|
|
pit[counter].read_state = 3;
|
|
|
|
pit[counter].read_latch = -1;
|
|
|
|
break;
|
|
|
|
case 3: /* read LSB followed by MSB */
|
|
|
|
ret = (pit[counter].read_latch & 0xff);
|
|
|
|
if (pit[counter].mode & 0x80) pit[counter].mode &= 7; /* moved here */
|
|
|
|
else
|
|
|
|
pit[counter].read_state = 0;
|
|
|
|
break;
|
|
|
|
case 1: /* read MSB */
|
|
|
|
ret = (pit[counter].read_latch >> 8) & 0xff;
|
|
|
|
pit[counter].read_latch = -1;
|
|
|
|
break;
|
|
|
|
case 2: /* read LSB */
|
|
|
|
ret = (pit[counter].read_latch & 0xff);
|
|
|
|
pit[counter].read_latch = -1;
|
|
|
|
break;
|
|
|
|
default:
|
|
|
|
ret=0;
|
|
|
|
E_Exit("Timer.cpp: error in readlatch");
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
return ret;
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
static void write_p43(Bit32u port,Bit8u val) {
|
|
|
|
if (val & 1) {
|
|
|
|
E_Exit("PIT:BCD Counter not supported");
|
|
|
|
}
|
|
|
|
Bitu latch=(val >> 6) & 0x03;
|
|
|
|
switch (latch) {
|
|
|
|
case 0:
|
|
|
|
case 1:
|
|
|
|
case 2:
|
|
|
|
if ((val & 0x30) == 0) {
|
|
|
|
/* Counter latch command */
|
|
|
|
counter_latch(latch);
|
|
|
|
} else {
|
|
|
|
pit[latch].read_state = (val >> 4) & 0x03;
|
|
|
|
pit[latch].write_state = (val >> 4) & 0x03;
|
|
|
|
pit[latch].mode = (val >> 1) & 0x07;
|
|
|
|
}
|
|
|
|
break;
|
|
|
|
case 3:
|
|
|
|
E_Exit("Special PIT Latch Read out thing");
|
|
|
|
}
|
|
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
/* The TIMER Part */
|
|
|
|
|
|
|
|
enum { T_TICK,T_MICRO,T_DELAY};
|
|
|
|
|
|
|
|
struct Timer {
|
|
|
|
Bitu type;
|
|
|
|
union {
|
|
|
|
struct {
|
|
|
|
TIMER_TickHandler handler;
|
|
|
|
} tick;
|
|
|
|
struct{
|
2009-05-02 23:27:47 +02:00
|
|
|
Bits left;
|
|
|
|
Bits total;
|
2009-05-02 23:03:37 +02:00
|
|
|
TIMER_MicroHandler handler;
|
|
|
|
} micro;
|
|
|
|
};
|
|
|
|
};
|
|
|
|
|
|
|
|
static Timer * first_timer=0;
|
|
|
|
static std::list<Timer *> Timers;
|
|
|
|
|
|
|
|
TIMER_Block * TIMER_RegisterTickHandler(TIMER_TickHandler handler) {
|
|
|
|
Timer * new_timer=new(Timer);
|
|
|
|
new_timer->type=T_TICK;
|
|
|
|
new_timer->tick.handler=handler;
|
|
|
|
Timers.push_front(new_timer);
|
|
|
|
return (TIMER_Block *)new_timer;
|
|
|
|
}
|
|
|
|
|
|
|
|
TIMER_Block * TIMER_RegisterMicroHandler(TIMER_MicroHandler handler,Bitu micro) {
|
|
|
|
Timer * new_timer=new(Timer);
|
|
|
|
new_timer->type=T_MICRO;
|
|
|
|
new_timer->micro.handler=handler;
|
|
|
|
Timers.push_front(new_timer);
|
2009-05-02 23:27:47 +02:00
|
|
|
TIMER_SetNewMicro(new_timer,micro);
|
2009-05-02 23:03:37 +02:00
|
|
|
return (TIMER_Block *)new_timer;
|
|
|
|
}
|
|
|
|
|
|
|
|
void TIMER_SetNewMicro(TIMER_Block * block,Bitu micro) {
|
|
|
|
Timer * timer=(Timer *)block;
|
|
|
|
if (timer->type!=T_MICRO) E_Exit("TIMER:Illegal handler type");
|
|
|
|
timer->micro.total=micro;
|
2009-05-02 23:27:47 +02:00
|
|
|
Bitu index=PIC_Index();
|
|
|
|
while ((1000-index)>micro) {
|
|
|
|
PIC_AddEvent(timer->micro.handler,micro);
|
|
|
|
micro+=micro;
|
|
|
|
index+=micro;
|
|
|
|
}
|
|
|
|
timer->micro.left=timer->micro.total-(1000-index);
|
2009-05-02 23:03:37 +02:00
|
|
|
}
|
|
|
|
|
2009-05-02 23:27:47 +02:00
|
|
|
void TIMER_AddTick(void) {
|
|
|
|
Bits index;
|
2009-05-02 23:20:05 +02:00
|
|
|
/* Check if there are timer handlers that need to be called */
|
2009-05-02 23:03:37 +02:00
|
|
|
std::list<Timer *>::iterator i;
|
|
|
|
for(i=Timers.begin(); i != Timers.end(); ++i) {
|
2009-05-02 23:20:05 +02:00
|
|
|
Timer * timers=(*i);
|
|
|
|
switch (timers->type) {
|
2009-05-02 23:03:37 +02:00
|
|
|
case T_TICK:
|
2009-05-02 23:27:47 +02:00
|
|
|
timers->tick.handler(1);
|
2009-05-02 23:03:37 +02:00
|
|
|
break;
|
|
|
|
case T_MICRO:
|
2009-05-02 23:27:47 +02:00
|
|
|
index=1000;
|
|
|
|
while (index>=timers->micro.left) {
|
|
|
|
PIC_AddEvent(timers->micro.handler,timers->micro.left);
|
|
|
|
index-=timers->micro.left;
|
|
|
|
timers->micro.left=timers->micro.total;
|
2009-05-02 23:03:37 +02:00
|
|
|
}
|
2009-05-02 23:27:47 +02:00
|
|
|
timers->micro.left-=index;
|
2009-05-02 23:03:37 +02:00
|
|
|
break;
|
|
|
|
default:
|
|
|
|
E_Exit("TIMER:Illegal handler type");
|
2009-05-02 23:27:47 +02:00
|
|
|
}
|
2009-05-02 23:03:37 +02:00
|
|
|
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2009-05-02 23:20:05 +02:00
|
|
|
void TIMER_Init(Section* sect) {
|
2009-05-02 23:03:37 +02:00
|
|
|
IO_RegisterWriteHandler(0x40,write_latch,"PIT Timer 0");
|
|
|
|
IO_RegisterWriteHandler(0x42,write_latch,"PIT Timer 2");
|
|
|
|
IO_RegisterWriteHandler(0x43,write_p43,"PIT Mode Control");
|
|
|
|
IO_RegisterReadHandler(0x40,read_latch,"PIT Timer 0");
|
|
|
|
// IO_RegisterReadHandler(0x41,read_p41,"PIT Timer 1");
|
|
|
|
IO_RegisterReadHandler(0x42,read_latch,"PIT Timer 2");
|
|
|
|
/* Setup Timer 0 */
|
|
|
|
pit[0].cntr=0x10000;
|
|
|
|
pit[0].write_state = 3;
|
|
|
|
pit[0].read_state = 3;
|
|
|
|
pit[0].read_latch=-1;
|
|
|
|
pit[0].write_latch=0;
|
|
|
|
pit[0].mode=3;
|
2009-05-02 23:27:47 +02:00
|
|
|
|
|
|
|
pit[0].micro=1000000/((float)PIT_TICK_RATE/(float)pit[0].cntr);
|
|
|
|
pit[2].micro=100;
|
|
|
|
PIC_AddEvent(PIT0_Event,pit[0].micro);
|
2009-05-02 23:03:37 +02:00
|
|
|
}
|
|
|
|
|