1 /*
2 * Copyright (C) 2005-2020 Anders Gavare. All rights reserved.
3 *
4 * Redistribution and use in source and binary forms, with or without
5 * modification, are permitted provided that the following conditions are met:
6 *
7 * 1. Redistributions of source code must retain the above copyright
8 * notice, this list of conditions and the following disclaimer.
9 * 2. Redistributions in binary form must reproduce the above copyright
10 * notice, this list of conditions and the following disclaimer in the
11 * documentation and/or other materials provided with the distribution.
12 * 3. The name of the author may not be used to endorse or promote products
13 * derived from this software without specific prior written permission.
14 *
15 * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
16 * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
17 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
18 * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
19 * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
20 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
21 * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
22 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
23 * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
24 * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
25 * SUCH DAMAGE.
26 *
27 *
28 * COMMENT: Intel 8253/8254 Programmable Interval Timer
29 *
30 * TODO/NOTE:
31 * The timers don't really count down. Timer 0 causes clock interrupts
32 * at a specific frequency, but reading the counter register would not
33 * result in anything meaningful.
34 *
35 * (Split counter[] into reset value and current value.)
36 */
37
38 #include <stdio.h>
39 #include <stdlib.h>
40 #include <string.h>
41
42 #include "cpu.h"
43 #include "device.h"
44 #include "emul.h"
45 #include "interrupt.h"
46 #include "machine.h"
47 #include "memory.h"
48 #include "misc.h"
49 #include "timer.h"
50
51 #include "thirdparty/i8253reg.h"
52
53
54 /* #define debug fatal */
55
56 #define DEV_8253_LENGTH 4
57 #define TICK_SHIFT 14
58
59
60 struct pit8253_data {
61 int in_use;
62
63 int counter_select;
64 uint8_t mode_byte;
65
66 int mode[3];
67 int counter[3];
68
69 int hz[3];
70
71 struct timer *timer0;
72 struct interrupt irq;
73 int pending_interrupts_timer0;
74 };
75
76
timer0_tick(struct timer * t,void * extra)77 static void timer0_tick(struct timer *t, void *extra)
78 {
79 struct pit8253_data *d = (struct pit8253_data *) extra;
80 d->pending_interrupts_timer0 ++;
81
82 /* printf("%i ", d->pending_interrupts_timer0); fflush(stdout); */
83 }
84
85
86 DEVICE_TICK(8253)
87 {
88 struct pit8253_data *d = (struct pit8253_data *) extra;
89
90 if (!d->in_use)
91 return;
92
93 // Generate interrupts regardless of (d->mode[0] & 0x0e)?
94 // (It seems like Linux/MALTA kernels like this.)
95 if (d->pending_interrupts_timer0 > 0)
96 INTERRUPT_ASSERT(d->irq);
97 }
98
99
100 DEVICE_ACCESS(8253)
101 {
102 struct pit8253_data *d = (struct pit8253_data *) extra;
103 uint64_t idata = 0, odata = 0;
104
105 if (writeflag == MEM_WRITE)
106 idata = memory_readmax64(cpu, data, len);
107
108 d->in_use = 1;
109
110 switch (relative_addr) {
111
112 case I8253_TIMER_CNTR0:
113 case I8253_TIMER_CNTR1:
114 case I8253_TIMER_CNTR2:
115 if (writeflag == MEM_WRITE) {
116 switch (d->mode_byte & 0x30) {
117 case I8253_TIMER_LSB:
118 case I8253_TIMER_16BIT:
119 d->counter[relative_addr] &= 0xff00;
120 d->counter[relative_addr] |= (idata & 0xff);
121 break;
122 case I8253_TIMER_MSB:
123 d->counter[relative_addr] &= 0x00ff;
124 d->counter[relative_addr] |= ((idata&0xff)<<8);
125 if (d->counter[relative_addr] != 0)
126 d->hz[relative_addr] = (int) (
127 I8253_TIMER_FREQ / (float)
128 d->counter[relative_addr] + 0.5);
129 else
130 d->hz[relative_addr] = 0;
131 debug("[ 8253: counter %i set to %i (%i Hz) "
132 "]\n", relative_addr, d->counter[
133 relative_addr], d->hz[relative_addr]);
134 switch (relative_addr) {
135 case 0: if (d->timer0 == NULL)
136 d->timer0 = timer_add(
137 d->hz[0], timer0_tick, d);
138 else
139 timer_update_frequency(
140 d->timer0, d->hz[0]);
141 break;
142 case 1: fatal("TODO: DMA refresh?\n");
143 exit(1);
144 case 2: fatal("TODO: 8253 tone generation?\n");
145 break;
146 }
147 break;
148 default:fatal("[ 8253: huh? writing to counter"
149 " %i but neither from msb nor lsb? ]\n",
150 relative_addr);
151 exit(1);
152 }
153 } else {
154 switch (d->mode_byte & 0x30) {
155 case I8253_TIMER_LSB:
156 case I8253_TIMER_16BIT:
157 odata = d->counter[relative_addr] & 0xff;
158 break;
159 case I8253_TIMER_MSB:
160 odata = (d->counter[relative_addr] >> 8) & 0xff;
161 break;
162 default:fatal("[ 8253: huh? reading from counter"
163 " %i but neither from msb nor lsb? ]\n",
164 relative_addr);
165 exit(1);
166 }
167 }
168
169 /* Switch from LSB to MSB, if accessing as 16-bit word: */
170 if ((d->mode_byte & 0x30) == I8253_TIMER_16BIT)
171 d->mode_byte &= ~I8253_TIMER_LSB;
172
173 break;
174
175 case I8253_TIMER_MODE:
176 if (writeflag == MEM_WRITE) {
177 d->mode_byte = idata;
178
179 d->counter_select = idata >> 6;
180 if (d->counter_select > 2) {
181 debug("[ 8253: attempt to select counter 3,"
182 " which doesn't exist. ]\n");
183 d->counter_select = 0;
184 }
185
186 d->mode[d->counter_select] = idata & 0x0e;
187
188 debug("[ 8253: select=%i mode=0x%x ",
189 d->counter_select, d->mode[d->counter_select]);
190 if (idata & 0x30) {
191 switch (idata & 0x30) {
192 case I8253_TIMER_LSB:
193 debug("LSB ");
194 break;
195 case I8253_TIMER_16BIT:
196 debug("LSB+");
197 // fall through
198 case I8253_TIMER_MSB:
199 debug("MSB ");
200 }
201 }
202 debug("]\n");
203
204 if (idata & I8253_TIMER_BCD) {
205 fatal("[ 8253: BCD not yet implemented ]\n");
206 exit(1);
207 }
208 } else {
209 debug("[ 8253: read; can this actually happen? ]\n");
210 odata = d->mode_byte;
211 }
212 break;
213
214 default:if (writeflag == MEM_WRITE) {
215 fatal("[ 8253: unimplemented write to address 0x%x"
216 " data=0x%02x ]\n", (int)relative_addr, (int)idata);
217 } else {
218 fatal("[ 8253: unimplemented read from address 0x%x "
219 "]\n", (int)relative_addr);
220 }
221 exit(1);
222 }
223
224 if (writeflag == MEM_READ)
225 memory_writemax64(cpu, data, len, odata);
226
227 return 1;
228 }
229
230
231 DEVINIT(8253)
232 {
233 struct pit8253_data *d;
234
235 CHECK_ALLOCATION(d = (struct pit8253_data *) malloc(sizeof(struct pit8253_data)));
236 memset(d, 0, sizeof(struct pit8253_data));
237
238 d->in_use = devinit->in_use;
239
240 INTERRUPT_CONNECT(devinit->interrupt_path, d->irq);
241
242 /* Don't cause interrupt, by default. */
243 d->mode[0] = I8253_TIMER_RATEGEN;
244 d->mode[1] = I8253_TIMER_RATEGEN;
245 d->mode[2] = I8253_TIMER_RATEGEN;
246
247 devinit->machine->isa_pic_data.pending_timer_interrupts =
248 &d->pending_interrupts_timer0;
249
250 memory_device_register(devinit->machine->memory, devinit->name,
251 devinit->addr, DEV_8253_LENGTH, dev_8253_access, (void *)d,
252 DM_DEFAULT, NULL);
253
254 machine_add_tickfunction(devinit->machine, dev_8253_tick,
255 d, TICK_SHIFT);
256
257 return 1;
258 }
259
260