2004-03-14 20:20:30 +08:00
/*
* QEMU 8253 / 8254 interval timer emulation
*
* Copyright ( c ) 2003 - 2004 Fabrice Bellard
*
* Permission is hereby granted , free of charge , to any person obtaining a copy
* of this software and associated documentation files ( the " Software " ) , to deal
* in the Software without restriction , including without limitation the rights
* to use , copy , modify , merge , publish , distribute , sublicense , and / or sell
* copies of the Software , and to permit persons to whom the Software is
* furnished to do so , subject to the following conditions :
*
* The above copyright notice and this permission notice shall be included in
* all copies or substantial portions of the Software .
*
* THE SOFTWARE IS PROVIDED " AS IS " , WITHOUT WARRANTY OF ANY KIND , EXPRESS OR
* IMPLIED , INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY ,
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT . IN NO EVENT SHALL
* THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM , DAMAGES OR OTHER
* LIABILITY , WHETHER IN AN ACTION OF CONTRACT , TORT OR OTHERWISE , ARISING FROM ,
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
* THE SOFTWARE .
*/
# include <stdlib.h>
# include <stdio.h>
# include <stdarg.h>
# include <string.h>
# include <getopt.h>
# include <inttypes.h>
# include <unistd.h>
# include <sys/mman.h>
# include <fcntl.h>
# include <signal.h>
# include <time.h>
# include <sys/time.h>
# include <malloc.h>
# include <termios.h>
# include <sys/poll.h>
# include <errno.h>
# include <sys/wait.h>
# include <netinet/in.h>
# include "cpu.h"
# include "vl.h"
# define RW_STATE_LSB 0
# define RW_STATE_MSB 1
# define RW_STATE_WORD0 2
# define RW_STATE_WORD1 3
# define RW_STATE_LATCHED_WORD0 4
# define RW_STATE_LATCHED_WORD1 5
PITChannelState pit_channels [ 3 ] ;
static int pit_get_count ( PITChannelState * s )
{
uint64_t d ;
int counter ;
d = muldiv64 ( cpu_get_ticks ( ) - s - > count_load_time , PIT_FREQ , ticks_per_sec ) ;
switch ( s - > mode ) {
case 0 :
case 1 :
case 4 :
case 5 :
counter = ( s - > count - d ) & 0xffff ;
break ;
case 3 :
/* XXX: may be incorrect for odd counts */
counter = s - > count - ( ( 2 * d ) % s - > count ) ;
break ;
default :
counter = s - > count - ( d % s - > count ) ;
break ;
}
return counter ;
}
/* get pit output bit */
int pit_get_out ( PITChannelState * s )
{
uint64_t d ;
int out ;
d = muldiv64 ( cpu_get_ticks ( ) - s - > count_load_time , PIT_FREQ , ticks_per_sec ) ;
switch ( s - > mode ) {
default :
case 0 :
out = ( d > = s - > count ) ;
break ;
case 1 :
out = ( d < s - > count ) ;
break ;
case 2 :
if ( ( d % s - > count ) = = 0 & & d ! = 0 )
out = 1 ;
else
out = 0 ;
break ;
case 3 :
out = ( d % s - > count ) < ( ( s - > count + 1 ) > > 1 ) ;
break ;
case 4 :
case 5 :
out = ( d = = s - > count ) ;
break ;
}
return out ;
}
/* get the number of 0 to 1 transitions we had since we call this
function */
/* XXX: maybe better to use ticks precision to avoid getting edges
twice if checks are done at very small intervals */
int pit_get_out_edges ( PITChannelState * s )
{
uint64_t d1 , d2 ;
int64_t ticks ;
int ret , v ;
ticks = cpu_get_ticks ( ) ;
d1 = muldiv64 ( s - > count_last_edge_check_time - s - > count_load_time ,
PIT_FREQ , ticks_per_sec ) ;
d2 = muldiv64 ( ticks - s - > count_load_time ,
PIT_FREQ , ticks_per_sec ) ;
s - > count_last_edge_check_time = ticks ;
switch ( s - > mode ) {
default :
case 0 :
if ( d1 < s - > count & & d2 > = s - > count )
ret = 1 ;
else
ret = 0 ;
break ;
case 1 :
ret = 0 ;
break ;
case 2 :
d1 / = s - > count ;
d2 / = s - > count ;
ret = d2 - d1 ;
break ;
case 3 :
v = s - > count - ( ( s - > count + 1 ) > > 1 ) ;
d1 = ( d1 + v ) / s - > count ;
d2 = ( d2 + v ) / s - > count ;
ret = d2 - d1 ;
break ;
case 4 :
case 5 :
if ( d1 < s - > count & & d2 > = s - > count )
ret = 1 ;
else
ret = 0 ;
break ;
}
return ret ;
}
/* val must be 0 or 1 */
void pit_set_gate ( PITChannelState * s , int val )
{
switch ( s - > mode ) {
default :
case 0 :
case 4 :
/* XXX: just disable/enable counting */
break ;
case 1 :
case 5 :
if ( s - > gate < val ) {
/* restart counting on rising edge */
s - > count_load_time = cpu_get_ticks ( ) ;
s - > count_last_edge_check_time = s - > count_load_time ;
}
break ;
case 2 :
case 3 :
if ( s - > gate < val ) {
/* restart counting on rising edge */
s - > count_load_time = cpu_get_ticks ( ) ;
s - > count_last_edge_check_time = s - > count_load_time ;
}
/* XXX: disable/enable counting */
break ;
}
s - > gate = val ;
}
static inline void pit_load_count ( PITChannelState * s , int val )
{
if ( val = = 0 )
val = 0x10000 ;
s - > count_load_time = cpu_get_ticks ( ) ;
s - > count_last_edge_check_time = s - > count_load_time ;
s - > count = val ;
if ( s = = & pit_channels [ 0 ] & & val < = pit_min_timer_count ) {
fprintf ( stderr ,
" \n WARNING: qemu: on your system, accurate timer emulation is impossible if its frequency is more than %d Hz. If using a 2.6 guest Linux kernel, you must patch asm/param.h to change HZ from 1000 to 100. \n \n " ,
PIT_FREQ / pit_min_timer_count ) ;
}
}
2004-03-15 05:46:48 +08:00
static void pit_ioport_write ( void * opaque , uint32_t addr , uint32_t val )
2004-03-14 20:20:30 +08:00
{
int channel , access ;
PITChannelState * s ;
addr & = 3 ;
if ( addr = = 3 ) {
channel = val > > 6 ;
if ( channel = = 3 )
return ;
s = & pit_channels [ channel ] ;
access = ( val > > 4 ) & 3 ;
switch ( access ) {
case 0 :
s - > latched_count = pit_get_count ( s ) ;
s - > rw_state = RW_STATE_LATCHED_WORD0 ;
break ;
default :
s - > mode = ( val > > 1 ) & 7 ;
s - > bcd = val & 1 ;
s - > rw_state = access - 1 + RW_STATE_LSB ;
break ;
}
} else {
s = & pit_channels [ addr ] ;
switch ( s - > rw_state ) {
case RW_STATE_LSB :
pit_load_count ( s , val ) ;
break ;
case RW_STATE_MSB :
pit_load_count ( s , val < < 8 ) ;
break ;
case RW_STATE_WORD0 :
case RW_STATE_WORD1 :
if ( s - > rw_state & 1 ) {
pit_load_count ( s , ( s - > latched_count & 0xff ) | ( val < < 8 ) ) ;
} else {
s - > latched_count = val ;
}
s - > rw_state ^ = 1 ;
break ;
}
}
}
2004-03-15 05:46:48 +08:00
static uint32_t pit_ioport_read ( void * opaque , uint32_t addr )
2004-03-14 20:20:30 +08:00
{
int ret , count ;
PITChannelState * s ;
addr & = 3 ;
s = & pit_channels [ addr ] ;
switch ( s - > rw_state ) {
case RW_STATE_LSB :
case RW_STATE_MSB :
case RW_STATE_WORD0 :
case RW_STATE_WORD1 :
count = pit_get_count ( s ) ;
if ( s - > rw_state & 1 )
ret = ( count > > 8 ) & 0xff ;
else
ret = count & 0xff ;
if ( s - > rw_state & 2 )
s - > rw_state ^ = 1 ;
break ;
default :
case RW_STATE_LATCHED_WORD0 :
case RW_STATE_LATCHED_WORD1 :
if ( s - > rw_state & 1 )
ret = s - > latched_count > > 8 ;
else
ret = s - > latched_count & 0xff ;
s - > rw_state ^ = 1 ;
break ;
}
return ret ;
}
2004-03-15 05:46:48 +08:00
void pit_init ( int base )
2004-03-14 20:20:30 +08:00
{
PITChannelState * s ;
int i ;
for ( i = 0 ; i < 3 ; i + + ) {
s = & pit_channels [ i ] ;
s - > mode = 3 ;
s - > gate = ( i ! = 2 ) ;
pit_load_count ( s , 0 ) ;
}
2004-03-15 05:46:48 +08:00
register_ioport_write ( base , 4 , 1 , pit_ioport_write , NULL ) ;
register_ioport_read ( base , 3 , 1 , pit_ioport_read , NULL ) ;
2004-03-14 20:20:30 +08:00
}