dosbox-wii/src/hardware/timer.cpp

289 lines
7.9 KiB
C++
Raw Normal View History

2009-05-02 23:03:37 +02:00
/*
2009-05-02 23:53:27 +02:00
* Copyright (C) 2002-2004 The DOSBox Team
2009-05-02 23:03:37 +02:00
*
* 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
2009-05-03 00:02:15 +02:00
* GNU General Public License for more details.
2009-05-02 23:03:37 +02:00
*
* 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.
*/
2009-05-03 00:08:43 +02:00
/* $Id: timer.cpp,v 1.30 2004/11/13 11:59:46 qbix79 Exp $ */
2009-05-02 23:03:37 +02:00
#include "dosbox.h"
#include "inout.h"
#include "pic.h"
#include "bios.h"
#include "mem.h"
#include "dosbox.h"
#include "mixer.h"
#include "timer.h"
2009-05-03 00:02:15 +02:00
#include "math.h"
static INLINE void BIN2BCD(Bit16u& val) {
Bit16u temp=val%10 + (((val/10)%10)<<4)+ (((val/100)%10)<<8) + (((val/1000)%10)<<12);
val=temp;
}
static INLINE void BCD2BIN(Bit16u& val) {
Bit16u temp= (val&0x0f) +((val>>4)&0x0f) *10 +((val>>8)&0x0f) *100 +((val>>12)&0x0f) *1000;
val=temp;
}
2009-05-02 23:03:37 +02:00
struct PIT_Block {
2009-05-02 23:27:47 +02:00
Bitu cntr;
2009-05-03 00:02:15 +02:00
float delay;
double start;
Bit16u read_latch;
Bit16u write_latch;
Bit8u mode;
2009-05-02 23:03:37 +02:00
Bit8u latch_mode;
Bit8u read_state;
Bit8u write_state;
2009-05-03 00:02:15 +02:00
bool bcd;
bool go_read_latch;
bool new_mode;
2009-05-02 23:03:37 +02:00
};
static PIT_Block pit[3];
2009-05-03 00:02:15 +02:00
static void PIT0_Event(Bitu val) {
2009-05-02 23:27:47 +02:00
PIC_ActivateIRQ(0);
2009-05-03 00:02:15 +02:00
if (pit[0].mode!=0) PIC_AddEvent(PIT0_Event,pit[0].delay);
}
static bool counter_output(Bitu counter) {
PIT_Block * p=&pit[counter];
double index=PIC_FullIndex()-p->start;
switch (p->mode) {
case 0:
if (p->new_mode) return false;
if (index>p->delay) return true;
else return false;
break;
case 2:
if (p->new_mode) return true;
index=fmod(index,(double)p->delay);
return index>0;
case 3:
if (p->new_mode) return true;
index=fmod(index,(double)p->delay);
return index*2<p->delay;
default:
LOG(LOG_PIT,LOG_ERROR)("Illegal Mode %d for reading output",p->mode);
return true;
}
2009-05-02 23:27:47 +02:00
}
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-03 00:02:15 +02:00
p->go_read_latch=false;
double index=PIC_FullIndex()-p->start;
2009-05-02 23:03:37 +02:00
switch (p->mode) {
2009-05-03 00:02:15 +02:00
case 4: /* Software Triggered Strobe */
2009-05-02 23:35:44 +02:00
case 0: /* Interrupt on Terminal Count */
/* Counter keeps on counting after passing terminal count */
2009-05-03 00:02:15 +02:00
if (index>p->delay) {
index-=p->delay;
index=fmod(index,(1000.0/PIT_TICK_RATE)*0x1000);
p->read_latch=(Bit16u)(0xffff-index*0xffff);
2009-05-02 23:35:44 +02:00
} else {
2009-05-03 00:02:15 +02:00
p->read_latch=(Bit16u)(p->cntr-index*(PIT_TICK_RATE/1000.0));
2009-05-02 23:35:44 +02:00
}
2009-05-02 23:27:47 +02:00
break;
2009-05-02 23:35:44 +02:00
case 2: /* Rate Generator */
2009-05-03 00:02:15 +02:00
index=fmod(index,(double)p->delay);
p->read_latch=(Bit16u)(p->cntr - (index/p->delay)*p->cntr);
2009-05-02 23:27:47 +02:00
break;
2009-05-02 23:35:44 +02:00
case 3: /* Square Wave Rate Generator */
2009-05-03 00:02:15 +02:00
index=fmod(index,(double)p->delay);
index*=2;
if (index>p->delay) index-=p->delay;
p->read_latch=(Bit16u)(p->cntr - (index/p->delay)*p->cntr);
2009-05-02 23:03:37 +02:00
break;
default:
2009-05-02 23:43:00 +02:00
LOG(LOG_PIT,LOG_ERROR)("Illegal Mode %d for reading counter %d",p->mode,counter);
2009-05-03 00:02:15 +02:00
p->read_latch=0xffff;
2009-05-02 23:03:37 +02:00
break;
}
}
2009-05-03 00:02:15 +02:00
static void write_latch(Bitu port,Bitu val,Bitu iolen) {
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];
2009-05-03 00:02:15 +02:00
if(p->bcd == true) BIN2BCD(p->write_latch);
2009-05-02 23:03:37 +02:00
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;
2009-05-03 00:02:15 +02:00
}
if (p->bcd==true) BCD2BIN(p->write_latch);
if (p->write_state != 0) {
if (p->write_latch == 0) {
if (p->bcd == false) p->cntr = 0x10000;
else p->cntr=9999;
} else p->cntr = p->write_latch;
p->start=PIC_FullIndex();
p->delay=(1000.0f/((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-03 00:08:43 +02:00
if (p->new_mode || p->mode == 0 ) {
2009-05-03 00:02:15 +02:00
p->new_mode=false;
PIC_AddEvent(PIT0_Event,p->delay);
} else LOG(LOG_PIT,LOG_NORMAL)("PIT 0 Timer set without new control word");
LOG(LOG_PIT,LOG_NORMAL)("PIT 0 Timer at %.2f Hz mode %d",1000.0/p->delay,p->mode);
2009-05-02 23:03:37 +02:00
break;
case 0x02: /* Timer hooked to PC-Speaker */
2009-05-02 23:35:44 +02:00
// LOG(LOG_PIT,"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:
2009-05-02 23:43:00 +02:00
LOG(LOG_PIT,LOG_ERROR)("PIT:Illegal timer selected for writing");
2009-05-02 23:03:37 +02:00
}
}
}
2009-05-03 00:02:15 +02:00
static Bitu read_latch(Bitu port,Bitu iolen) {
2009-05-02 23:03:37 +02:00
Bit32u counter=port-0x40;
2009-05-03 00:02:15 +02:00
if (pit[counter].go_read_latch == true)
2009-05-02 23:03:37 +02:00
counter_latch(counter);
Bit8u ret;
2009-05-03 00:02:15 +02:00
if( pit[counter].bcd == true) BIN2BCD(pit[counter].read_latch);
2009-05-02 23:03:37 +02:00
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;
2009-05-03 00:02:15 +02:00
pit[counter].go_read_latch = true;
2009-05-02 23:03:37 +02:00
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;
2009-05-02 23:43:00 +02:00
case 1: /* read LSB */
ret = (pit[counter].read_latch & 0xff);
2009-05-03 00:02:15 +02:00
pit[counter].go_read_latch = true;
2009-05-02 23:03:37 +02:00
break;
2009-05-02 23:43:00 +02:00
case 2: /* read MSB */
ret = (pit[counter].read_latch >> 8) & 0xff;
2009-05-03 00:02:15 +02:00
pit[counter].go_read_latch = true;
2009-05-02 23:03:37 +02:00
break;
default:
ret=0;
E_Exit("Timer.cpp: error in readlatch");
break;
2009-05-03 00:02:15 +02:00
}
if( pit[counter].bcd == true) BCD2BIN(pit[counter].read_latch);
return ret;
2009-05-02 23:03:37 +02:00
}
2009-05-03 00:02:15 +02:00
static void write_p43(Bitu port,Bitu val,Bitu iolen) {
2009-05-02 23:03:37 +02:00
Bitu latch=(val >> 6) & 0x03;
switch (latch) {
case 0:
case 1:
case 2:
2009-05-03 00:02:15 +02:00
pit[latch].bcd = (val&1)>0;
if (val & 1) {
if(pit[latch].cntr>=9999) pit[latch].cntr=9999;
}
2009-05-02 23:03:37 +02:00
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;
2009-05-03 00:02:15 +02:00
if (pit[latch].mode>5)
pit[latch].mode-=4; //6,7 become 2 and 3
if (latch==0) {
PIC_RemoveEvents(PIT0_Event);
if (!counter_output(0) && pit[latch].mode)
PIC_ActivateIRQ(0);
}
pit[latch].new_mode = true;
2009-05-02 23:03:37 +02:00
}
break;
case 3:
2009-05-02 23:35:44 +02:00
if ((val & 0x20)==0) { /* Latch multiple pit counters */
if (val & 0x02) counter_latch(0);
if (val & 0x04) counter_latch(1);
if (val & 0x08) counter_latch(2);
2009-05-02 23:43:00 +02:00
} else if ((val & 0x10)==0) { /* Latch status words */
LOG(LOG_PIT,LOG_ERROR)("Unsupported Latch status word call");
} else LOG(LOG_PIT,LOG_ERROR)("Unhandled command:%X",val);
2009-05-02 23:35:44 +02:00
break;
2009-05-02 23:03:37 +02:00
}
}
2009-05-02 23:20:05 +02:00
void TIMER_Init(Section* sect) {
2009-05-03 00:02:15 +02:00
IO_RegisterWriteHandler(0x40,write_latch,IO_MB);
// IO_RegisterWriteHandler(0x41,write_latch,IO_MB);
IO_RegisterWriteHandler(0x42,write_latch,IO_MB);
IO_RegisterWriteHandler(0x43,write_p43,IO_MB);
IO_RegisterReadHandler(0x40,read_latch,IO_MB);
IO_RegisterReadHandler(0x41,read_latch,IO_MB);
IO_RegisterReadHandler(0x42,read_latch,IO_MB);
2009-05-02 23:03:37 +02:00
/* Setup Timer 0 */
pit[0].cntr=0x10000;
pit[0].write_state = 3;
pit[0].read_state = 3;
2009-05-03 00:02:15 +02:00
pit[0].read_latch=0;
2009-05-02 23:03:37 +02:00
pit[0].write_latch=0;
pit[0].mode=3;
2009-05-03 00:02:15 +02:00
pit[0].bcd = false;
pit[0].go_read_latch = true;
pit[1].bcd = false;
pit[1].write_state = 1;
pit[1].read_state = 1;
pit[1].go_read_latch = true;
pit[1].cntr = 18;
pit[1].mode = 2;
pit[1].write_state = 3;
2009-05-02 23:27:47 +02:00
2009-05-03 00:02:15 +02:00
pit[2].read_latch=0; /* MadTv1 */
2009-05-02 23:53:27 +02:00
pit[2].write_state = 3; /* Chuck Yeager */
2009-05-03 00:02:15 +02:00
pit[2].read_state = 3;
2009-05-02 23:53:27 +02:00
pit[2].mode=3;
2009-05-03 00:02:15 +02:00
pit[2].bcd=false;
pit[2].cntr=1320;
pit[2].go_read_latch=true;
pit[0].delay=(1000.0f/((float)PIT_TICK_RATE/(float)pit[0].cntr));
pit[1].delay=(1000.0f/((float)PIT_TICK_RATE/(float)pit[1].cntr));
pit[2].delay=(1000.0f/((float)PIT_TICK_RATE/(float)pit[2].cntr));
2009-05-02 23:43:00 +02:00
2009-05-03 00:02:15 +02:00
PIC_AddEvent(PIT0_Event,pit[0].delay);
2009-05-02 23:03:37 +02:00
}