Statistics
| Branch: | Revision:

root / hw / i8254.c @ 80cabfad

History | View | Annotate | Download (7.6 kB)

1
/*
2
 * QEMU 8253/8254 interval timer emulation
3
 * 
4
 * Copyright (c) 2003-2004 Fabrice Bellard
5
 * 
6
 * Permission is hereby granted, free of charge, to any person obtaining a copy
7
 * of this software and associated documentation files (the "Software"), to deal
8
 * in the Software without restriction, including without limitation the rights
9
 * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
10
 * copies of the Software, and to permit persons to whom the Software is
11
 * furnished to do so, subject to the following conditions:
12
 *
13
 * The above copyright notice and this permission notice shall be included in
14
 * all copies or substantial portions of the Software.
15
 *
16
 * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
17
 * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
18
 * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
19
 * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
20
 * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
21
 * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
22
 * THE SOFTWARE.
23
 */
24
#include <stdlib.h>
25
#include <stdio.h>
26
#include <stdarg.h>
27
#include <string.h>
28
#include <getopt.h>
29
#include <inttypes.h>
30
#include <unistd.h>
31
#include <sys/mman.h>
32
#include <fcntl.h>
33
#include <signal.h>
34
#include <time.h>
35
#include <sys/time.h>
36
#include <malloc.h>
37
#include <termios.h>
38
#include <sys/poll.h>
39
#include <errno.h>
40
#include <sys/wait.h>
41
#include <netinet/in.h>
42

    
43
#include "cpu.h"
44
#include "vl.h"
45

    
46
#define RW_STATE_LSB 0
47
#define RW_STATE_MSB 1
48
#define RW_STATE_WORD0 2
49
#define RW_STATE_WORD1 3
50
#define RW_STATE_LATCHED_WORD0 4
51
#define RW_STATE_LATCHED_WORD1 5
52

    
53
PITChannelState pit_channels[3];
54

    
55
static int pit_get_count(PITChannelState *s)
56
{
57
    uint64_t d;
58
    int counter;
59

    
60
    d = muldiv64(cpu_get_ticks() - s->count_load_time, PIT_FREQ, ticks_per_sec);
61
    switch(s->mode) {
62
    case 0:
63
    case 1:
64
    case 4:
65
    case 5:
66
        counter = (s->count - d) & 0xffff;
67
        break;
68
    case 3:
69
        /* XXX: may be incorrect for odd counts */
70
        counter = s->count - ((2 * d) % s->count);
71
        break;
72
    default:
73
        counter = s->count - (d % s->count);
74
        break;
75
    }
76
    return counter;
77
}
78

    
79
/* get pit output bit */
80
int pit_get_out(PITChannelState *s)
81
{
82
    uint64_t d;
83
    int out;
84

    
85
    d = muldiv64(cpu_get_ticks() - s->count_load_time, PIT_FREQ, ticks_per_sec);
86
    switch(s->mode) {
87
    default:
88
    case 0:
89
        out = (d >= s->count);
90
        break;
91
    case 1:
92
        out = (d < s->count);
93
        break;
94
    case 2:
95
        if ((d % s->count) == 0 && d != 0)
96
            out = 1;
97
        else
98
            out = 0;
99
        break;
100
    case 3:
101
        out = (d % s->count) < ((s->count + 1) >> 1);
102
        break;
103
    case 4:
104
    case 5:
105
        out = (d == s->count);
106
        break;
107
    }
108
    return out;
109
}
110

    
111
/* get the number of 0 to 1 transitions we had since we call this
112
   function */
113
/* XXX: maybe better to use ticks precision to avoid getting edges
114
   twice if checks are done at very small intervals */
115
int pit_get_out_edges(PITChannelState *s)
116
{
117
    uint64_t d1, d2;
118
    int64_t ticks;
119
    int ret, v;
120

    
121
    ticks = cpu_get_ticks();
122
    d1 = muldiv64(s->count_last_edge_check_time - s->count_load_time, 
123
                 PIT_FREQ, ticks_per_sec);
124
    d2 = muldiv64(ticks - s->count_load_time, 
125
                  PIT_FREQ, ticks_per_sec);
126
    s->count_last_edge_check_time = ticks;
127
    switch(s->mode) {
128
    default:
129
    case 0:
130
        if (d1 < s->count && d2 >= s->count)
131
            ret = 1;
132
        else
133
            ret = 0;
134
        break;
135
    case 1:
136
        ret = 0;
137
        break;
138
    case 2:
139
        d1 /= s->count;
140
        d2 /= s->count;
141
        ret = d2 - d1;
142
        break;
143
    case 3:
144
        v = s->count - ((s->count + 1) >> 1);
145
        d1 = (d1 + v) / s->count;
146
        d2 = (d2 + v) / s->count;
147
        ret = d2 - d1;
148
        break;
149
    case 4:
150
    case 5:
151
        if (d1 < s->count && d2 >= s->count)
152
            ret = 1;
153
        else
154
            ret = 0;
155
        break;
156
    }
157
    return ret;
158
}
159

    
160
/* val must be 0 or 1 */
161
void pit_set_gate(PITChannelState *s, int val)
162
{
163
    switch(s->mode) {
164
    default:
165
    case 0:
166
    case 4:
167
        /* XXX: just disable/enable counting */
168
        break;
169
    case 1:
170
    case 5:
171
        if (s->gate < val) {
172
            /* restart counting on rising edge */
173
            s->count_load_time = cpu_get_ticks();
174
            s->count_last_edge_check_time = s->count_load_time;
175
        }
176
        break;
177
    case 2:
178
    case 3:
179
        if (s->gate < val) {
180
            /* restart counting on rising edge */
181
            s->count_load_time = cpu_get_ticks();
182
            s->count_last_edge_check_time = s->count_load_time;
183
        }
184
        /* XXX: disable/enable counting */
185
        break;
186
    }
187
    s->gate = val;
188
}
189

    
190
static inline void pit_load_count(PITChannelState *s, int val)
191
{
192
    if (val == 0)
193
        val = 0x10000;
194
    s->count_load_time = cpu_get_ticks();
195
    s->count_last_edge_check_time = s->count_load_time;
196
    s->count = val;
197
    if (s == &pit_channels[0] && val <= pit_min_timer_count) {
198
        fprintf(stderr, 
199
                "\nWARNING: 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", 
200
                PIT_FREQ / pit_min_timer_count);
201
    }
202
}
203

    
204
void pit_ioport_write(CPUState *env, uint32_t addr, uint32_t val)
205
{
206
    int channel, access;
207
    PITChannelState *s;
208

    
209
    addr &= 3;
210
    if (addr == 3) {
211
        channel = val >> 6;
212
        if (channel == 3)
213
            return;
214
        s = &pit_channels[channel];
215
        access = (val >> 4) & 3;
216
        switch(access) {
217
        case 0:
218
            s->latched_count = pit_get_count(s);
219
            s->rw_state = RW_STATE_LATCHED_WORD0;
220
            break;
221
        default:
222
            s->mode = (val >> 1) & 7;
223
            s->bcd = val & 1;
224
            s->rw_state = access - 1 +  RW_STATE_LSB;
225
            break;
226
        }
227
    } else {
228
        s = &pit_channels[addr];
229
        switch(s->rw_state) {
230
        case RW_STATE_LSB:
231
            pit_load_count(s, val);
232
            break;
233
        case RW_STATE_MSB:
234
            pit_load_count(s, val << 8);
235
            break;
236
        case RW_STATE_WORD0:
237
        case RW_STATE_WORD1:
238
            if (s->rw_state & 1) {
239
                pit_load_count(s, (s->latched_count & 0xff) | (val << 8));
240
            } else {
241
                s->latched_count = val;
242
            }
243
            s->rw_state ^= 1;
244
            break;
245
        }
246
    }
247
}
248

    
249
uint32_t pit_ioport_read(CPUState *env, uint32_t addr)
250
{
251
    int ret, count;
252
    PITChannelState *s;
253
    
254
    addr &= 3;
255
    s = &pit_channels[addr];
256
    switch(s->rw_state) {
257
    case RW_STATE_LSB:
258
    case RW_STATE_MSB:
259
    case RW_STATE_WORD0:
260
    case RW_STATE_WORD1:
261
        count = pit_get_count(s);
262
        if (s->rw_state & 1)
263
            ret = (count >> 8) & 0xff;
264
        else
265
            ret = count & 0xff;
266
        if (s->rw_state & 2)
267
            s->rw_state ^= 1;
268
        break;
269
    default:
270
    case RW_STATE_LATCHED_WORD0:
271
    case RW_STATE_LATCHED_WORD1:
272
        if (s->rw_state & 1)
273
            ret = s->latched_count >> 8;
274
        else
275
            ret = s->latched_count & 0xff;
276
        s->rw_state ^= 1;
277
        break;
278
    }
279
    return ret;
280
}
281

    
282
void pit_init(void)
283
{
284
    PITChannelState *s;
285
    int i;
286

    
287
    for(i = 0;i < 3; i++) {
288
        s = &pit_channels[i];
289
        s->mode = 3;
290
        s->gate = (i != 2);
291
        pit_load_count(s, 0);
292
    }
293

    
294
    register_ioport_write(0x40, 4, pit_ioport_write, 1);
295
    register_ioport_read(0x40, 3, pit_ioport_read, 1);
296
}
297