mirror of https://github.com/xqemu/xqemu.git
more precise timer emulation - fixed NE2000 probe problems - added VLTMPDIR support
git-svn-id: svn://svn.savannah.nongnu.org/qemu/trunk@286 c046a42c-6fe2-441c-8c8c-71466251a162
This commit is contained in:
parent
a6f816d697
commit
87858c89ca
160
vl.c
160
vl.c
|
@ -745,17 +745,19 @@ void pic_init(void)
|
||||||
#define RW_STATE_LATCHED_WORD1 5
|
#define RW_STATE_LATCHED_WORD1 5
|
||||||
|
|
||||||
typedef struct PITChannelState {
|
typedef struct PITChannelState {
|
||||||
uint16_t count;
|
int count; /* can be 65536 */
|
||||||
uint16_t latched_count;
|
uint16_t latched_count;
|
||||||
uint8_t rw_state;
|
uint8_t rw_state;
|
||||||
uint8_t mode;
|
uint8_t mode;
|
||||||
uint8_t bcd; /* not supported */
|
uint8_t bcd; /* not supported */
|
||||||
uint8_t gate; /* timer start */
|
uint8_t gate; /* timer start */
|
||||||
int64_t count_load_time;
|
int64_t count_load_time;
|
||||||
|
int64_t count_last_edge_check_time;
|
||||||
} PITChannelState;
|
} PITChannelState;
|
||||||
|
|
||||||
PITChannelState pit_channels[3];
|
PITChannelState pit_channels[3];
|
||||||
int speaker_data_on;
|
int speaker_data_on;
|
||||||
|
int pit_min_timer_count = 0;
|
||||||
|
|
||||||
int64_t ticks_per_sec;
|
int64_t ticks_per_sec;
|
||||||
|
|
||||||
|
@ -785,13 +787,36 @@ void cpu_calibrate_ticks(void)
|
||||||
ticks_per_sec = (ticks * 1000000LL + (usec >> 1)) / usec;
|
ticks_per_sec = (ticks * 1000000LL + (usec >> 1)) / usec;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/* compute with 96 bit intermediate result: (a*b)/c */
|
||||||
|
static uint64_t muldiv64(uint64_t a, uint32_t b, uint32_t c)
|
||||||
|
{
|
||||||
|
union {
|
||||||
|
uint64_t ll;
|
||||||
|
struct {
|
||||||
|
#ifdef WORDS_BIGENDIAN
|
||||||
|
uint32_t high, low;
|
||||||
|
#else
|
||||||
|
uint32_t low, high;
|
||||||
|
#endif
|
||||||
|
} l;
|
||||||
|
} u, res;
|
||||||
|
uint64_t rl, rh;
|
||||||
|
|
||||||
|
u.ll = a;
|
||||||
|
rl = (uint64_t)u.l.low * (uint64_t)b;
|
||||||
|
rh = (uint64_t)u.l.high * (uint64_t)b;
|
||||||
|
rh += (rl >> 32);
|
||||||
|
res.l.high = rh / c;
|
||||||
|
res.l.low = (((rh % c) << 32) + (rl & 0xffffffff)) / c;
|
||||||
|
return res.ll;
|
||||||
|
}
|
||||||
|
|
||||||
static int pit_get_count(PITChannelState *s)
|
static int pit_get_count(PITChannelState *s)
|
||||||
{
|
{
|
||||||
int64_t d;
|
uint64_t d;
|
||||||
int counter;
|
int counter;
|
||||||
|
|
||||||
d = ((cpu_get_ticks() - s->count_load_time) * PIT_FREQ) /
|
d = muldiv64(cpu_get_ticks() - s->count_load_time, PIT_FREQ, ticks_per_sec);
|
||||||
ticks_per_sec;
|
|
||||||
switch(s->mode) {
|
switch(s->mode) {
|
||||||
case 0:
|
case 0:
|
||||||
case 1:
|
case 1:
|
||||||
|
@ -809,11 +834,10 @@ static int pit_get_count(PITChannelState *s)
|
||||||
/* get pit output bit */
|
/* get pit output bit */
|
||||||
static int pit_get_out(PITChannelState *s)
|
static int pit_get_out(PITChannelState *s)
|
||||||
{
|
{
|
||||||
int64_t d;
|
uint64_t d;
|
||||||
int out;
|
int out;
|
||||||
|
|
||||||
d = ((cpu_get_ticks() - s->count_load_time) * PIT_FREQ) /
|
d = muldiv64(cpu_get_ticks() - s->count_load_time, PIT_FREQ, ticks_per_sec);
|
||||||
ticks_per_sec;
|
|
||||||
switch(s->mode) {
|
switch(s->mode) {
|
||||||
default:
|
default:
|
||||||
case 0:
|
case 0:
|
||||||
|
@ -839,6 +863,69 @@ static int pit_get_out(PITChannelState *s)
|
||||||
return out;
|
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 */
|
||||||
|
static 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);
|
||||||
|
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;
|
||||||
|
}
|
||||||
|
|
||||||
|
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,
|
||||||
|
"\nWARNING: vl: on your system, accurate timer emulation is impossible if its frequency is more than %d Hz. If using a 2.5.xx Linux kernel, you must patch asm/param.h to change HZ from 1000 to 100.\n\n",
|
||||||
|
PIT_FREQ / pit_min_timer_count);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
void pit_ioport_write(CPUX86State *env, uint32_t addr, uint32_t val)
|
void pit_ioport_write(CPUX86State *env, uint32_t addr, uint32_t val)
|
||||||
{
|
{
|
||||||
int channel, access;
|
int channel, access;
|
||||||
|
@ -857,27 +944,24 @@ void pit_ioport_write(CPUX86State *env, uint32_t addr, uint32_t val)
|
||||||
s->rw_state = RW_STATE_LATCHED_WORD0;
|
s->rw_state = RW_STATE_LATCHED_WORD0;
|
||||||
break;
|
break;
|
||||||
default:
|
default:
|
||||||
|
s->mode = (val >> 1) & 7;
|
||||||
|
s->bcd = val & 1;
|
||||||
s->rw_state = access - 1 + RW_STATE_LSB;
|
s->rw_state = access - 1 + RW_STATE_LSB;
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
s->mode = (val >> 1) & 7;
|
|
||||||
s->bcd = val & 1;
|
|
||||||
} else {
|
} else {
|
||||||
s = &pit_channels[addr];
|
s = &pit_channels[addr];
|
||||||
switch(s->rw_state) {
|
switch(s->rw_state) {
|
||||||
case RW_STATE_LSB:
|
case RW_STATE_LSB:
|
||||||
s->count_load_time = cpu_get_ticks();
|
pit_load_count(s, val);
|
||||||
s->count = val;
|
|
||||||
break;
|
break;
|
||||||
case RW_STATE_MSB:
|
case RW_STATE_MSB:
|
||||||
s->count_load_time = cpu_get_ticks();
|
pit_load_count(s, val << 8);
|
||||||
s->count = (val << 8);
|
|
||||||
break;
|
break;
|
||||||
case RW_STATE_WORD0:
|
case RW_STATE_WORD0:
|
||||||
case RW_STATE_WORD1:
|
case RW_STATE_WORD1:
|
||||||
if (s->rw_state & 1) {
|
if (s->rw_state & 1) {
|
||||||
s->count_load_time = cpu_get_ticks();
|
pit_load_count(s, (s->latched_count & 0xff) | (val << 8));
|
||||||
s->count = (s->latched_count & 0xff) | (val << 8);
|
|
||||||
} else {
|
} else {
|
||||||
s->latched_count = val;
|
s->latched_count = val;
|
||||||
}
|
}
|
||||||
|
@ -935,16 +1019,23 @@ uint32_t speaker_ioport_read(CPUX86State *env, uint32_t addr)
|
||||||
|
|
||||||
void pit_init(void)
|
void pit_init(void)
|
||||||
{
|
{
|
||||||
pit_channels[0].gate = 1;
|
PITChannelState *s;
|
||||||
pit_channels[1].gate = 1;
|
int i;
|
||||||
pit_channels[2].gate = 0;
|
|
||||||
|
cpu_calibrate_ticks();
|
||||||
|
|
||||||
|
for(i = 0;i < 3; i++) {
|
||||||
|
s = &pit_channels[i];
|
||||||
|
s->mode = 3;
|
||||||
|
s->gate = (i != 2);
|
||||||
|
pit_load_count(s, 0);
|
||||||
|
}
|
||||||
|
|
||||||
register_ioport_writeb(0x40, 4, pit_ioport_write);
|
register_ioport_writeb(0x40, 4, pit_ioport_write);
|
||||||
register_ioport_readb(0x40, 3, pit_ioport_read);
|
register_ioport_readb(0x40, 3, pit_ioport_read);
|
||||||
|
|
||||||
register_ioport_readb(0x61, 1, speaker_ioport_read);
|
register_ioport_readb(0x61, 1, speaker_ioport_read);
|
||||||
register_ioport_writeb(0x61, 1, speaker_ioport_write);
|
register_ioport_writeb(0x61, 1, speaker_ioport_write);
|
||||||
cpu_calibrate_ticks();
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/***********************************************************/
|
/***********************************************************/
|
||||||
|
@ -1462,6 +1553,8 @@ void ne2000_ioport_write(CPUX86State *env, uint32_t addr, uint32_t val)
|
||||||
s->rcnt == 0) {
|
s->rcnt == 0) {
|
||||||
s->isr |= ENISR_RDC;
|
s->isr |= ENISR_RDC;
|
||||||
ne2000_update_irq(s);
|
ne2000_update_irq(s);
|
||||||
|
/* XXX: find a better solution for irqs */
|
||||||
|
cpu_x86_interrupt(global_env);
|
||||||
}
|
}
|
||||||
if (val & E8390_TRANS) {
|
if (val & E8390_TRANS) {
|
||||||
net_send_packet(s, s->mem + (s->tpsr << 8), s->tcnt);
|
net_send_packet(s, s->mem + (s->tpsr << 8), s->tcnt);
|
||||||
|
@ -1671,13 +1764,23 @@ static void host_segv_handler(int host_signum, siginfo_t *info,
|
||||||
}
|
}
|
||||||
|
|
||||||
static int timer_irq_pending;
|
static int timer_irq_pending;
|
||||||
|
static int timer_irq_count;
|
||||||
|
|
||||||
static void host_alarm_handler(int host_signum, siginfo_t *info,
|
static void host_alarm_handler(int host_signum, siginfo_t *info,
|
||||||
void *puc)
|
void *puc)
|
||||||
{
|
{
|
||||||
|
/* NOTE: since usually the OS asks a 100 Hz clock, there can be
|
||||||
|
some drift between cpu_get_ticks() and the interrupt time. So
|
||||||
|
we queue some interrupts to avoid missing some */
|
||||||
|
timer_irq_count += pit_get_out_edges(&pit_channels[0]);
|
||||||
|
if (timer_irq_count) {
|
||||||
|
if (timer_irq_count > 2)
|
||||||
|
timer_irq_count = 2;
|
||||||
|
timer_irq_count--;
|
||||||
/* just exit from the cpu to have a chance to handle timers */
|
/* just exit from the cpu to have a chance to handle timers */
|
||||||
cpu_x86_interrupt(global_env);
|
cpu_x86_interrupt(global_env);
|
||||||
timer_irq_pending = 1;
|
timer_irq_pending = 1;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
void help(void)
|
void help(void)
|
||||||
|
@ -1705,6 +1808,7 @@ int main(int argc, char **argv)
|
||||||
struct sigaction act;
|
struct sigaction act;
|
||||||
struct itimerval itv;
|
struct itimerval itv;
|
||||||
CPUX86State *env;
|
CPUX86State *env;
|
||||||
|
const char *tmpdir;
|
||||||
|
|
||||||
/* we never want that malloc() uses mmap() */
|
/* we never want that malloc() uses mmap() */
|
||||||
mallopt(M_MMAP_THRESHOLD, 4096 * 1024);
|
mallopt(M_MMAP_THRESHOLD, 4096 * 1024);
|
||||||
|
@ -1749,14 +1853,19 @@ int main(int argc, char **argv)
|
||||||
net_init();
|
net_init();
|
||||||
|
|
||||||
/* init the memory */
|
/* init the memory */
|
||||||
strcpy(phys_ram_file, "/tmp/vlXXXXXX");
|
tmpdir = getenv("VLTMPDIR");
|
||||||
|
if (!tmpdir)
|
||||||
|
tmpdir = "/tmp";
|
||||||
|
snprintf(phys_ram_file, sizeof(phys_ram_file), "%s/vlXXXXXX", tmpdir);
|
||||||
if (mkstemp(phys_ram_file) < 0) {
|
if (mkstemp(phys_ram_file) < 0) {
|
||||||
fprintf(stderr, "Could not create temporary memory file\n");
|
fprintf(stderr, "Could not create temporary memory file '%s'\n",
|
||||||
|
phys_ram_file);
|
||||||
exit(1);
|
exit(1);
|
||||||
}
|
}
|
||||||
phys_ram_fd = open(phys_ram_file, O_CREAT | O_TRUNC | O_RDWR, 0600);
|
phys_ram_fd = open(phys_ram_file, O_CREAT | O_TRUNC | O_RDWR, 0600);
|
||||||
if (phys_ram_fd < 0) {
|
if (phys_ram_fd < 0) {
|
||||||
fprintf(stderr, "Could not open temporary memory file\n");
|
fprintf(stderr, "Could not open temporary memory file '%s'\n",
|
||||||
|
phys_ram_file);
|
||||||
exit(1);
|
exit(1);
|
||||||
}
|
}
|
||||||
ftruncate(phys_ram_fd, phys_ram_size);
|
ftruncate(phys_ram_fd, phys_ram_size);
|
||||||
|
@ -1856,10 +1965,15 @@ int main(int argc, char **argv)
|
||||||
env->eflags = 0x2;
|
env->eflags = 0x2;
|
||||||
|
|
||||||
itv.it_interval.tv_sec = 0;
|
itv.it_interval.tv_sec = 0;
|
||||||
itv.it_interval.tv_usec = 10 * 1000;
|
itv.it_interval.tv_usec = 1000;
|
||||||
itv.it_value.tv_sec = 0;
|
itv.it_value.tv_sec = 0;
|
||||||
itv.it_value.tv_usec = 10 * 1000;
|
itv.it_value.tv_usec = 10 * 1000;
|
||||||
setitimer(ITIMER_REAL, &itv, NULL);
|
setitimer(ITIMER_REAL, &itv, NULL);
|
||||||
|
/* we probe the tick duration of the kernel to inform the user if
|
||||||
|
the emulated kernel requested a too high timer frequency */
|
||||||
|
getitimer(ITIMER_REAL, &itv);
|
||||||
|
pit_min_timer_count = ((uint64_t)itv.it_interval.tv_usec * PIT_FREQ) /
|
||||||
|
1000000;
|
||||||
|
|
||||||
for(;;) {
|
for(;;) {
|
||||||
struct pollfd ufds[2], *pf, *serial_ufd, *net_ufd;
|
struct pollfd ufds[2], *pf, *serial_ufd, *net_ufd;
|
||||||
|
|
Loading…
Reference in New Issue