/[gxemul]/upstream/0.4.4/src/devices/dev_adb.c
This is repository of my old source code which isn't updated any more. Go to git.rot13.org for current projects!
ViewVC logotype

Contents of /upstream/0.4.4/src/devices/dev_adb.c

Parent Directory Parent Directory | Revision Log Revision Log


Revision 35 - (show annotations)
Mon Oct 8 16:21:26 2007 UTC (16 years, 8 months ago) by dpavlin
File MIME type: text/plain
File size: 11500 byte(s)
0.4.4
1 /*
2 * Copyright (C) 2005-2007 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 * $Id: dev_adb.c,v 1.12 2007/01/28 11:29:52 debug Exp $
29 *
30 * ADB (Apple Desktop Bus) controller.
31 *
32 * Based on intuition from reverse-engineering NetBSD/macppc source code,
33 * so it probably only works with that OS.
34 *
35 * The comment "OK" means that 100% of the functionality used by NetBSD/macppc
36 * is covered.
37 *
38 * TODO:
39 * o) Clean up, don't hardcode values.
40 * o) Convert into a separate controller, bus, device architecture.
41 */
42
43 #include <stdio.h>
44 #include <stdlib.h>
45 #include <string.h>
46 #include <sys/time.h>
47
48 #include "console.h"
49 #include "cpu.h"
50 #include "device.h"
51 #include "interrupt.h"
52 #include "machine.h"
53 #include "memory.h"
54 #include "misc.h"
55
56 #include "adb_viareg.h"
57
58
59 #define debug fatal
60 /* #define ADB_DEBUG */
61
62
63 #define TICK_SHIFT 17
64 #define DEV_ADB_LENGTH 0x2000
65
66 #define N_VIA_REGS 0x10
67 #define VIA_REG_SHIFT 9
68
69 #define MAX_BUF 100
70
71
72 static char *via_regname[N_VIA_REGS] = {
73 "vBufB", "vBufA", "vDirB", "vDirA",
74 "vT1C", "vT1CH", "vT1L", "vT1LH",
75 "vT2C", "vT2CH", "vSR", "vACR",
76 "vPCR", "vIFR", "vIER", "(unknown)" };
77
78 struct adb_data {
79 struct interrupt irq;
80 int int_asserted;
81
82 int kbd_dev;
83
84 long long transfer_nr;
85
86 uint8_t reg[N_VIA_REGS];
87
88 int cur_output_offset;
89 uint8_t output_buf[MAX_BUF];
90
91 int cur_input_offset;
92 int cur_input_length;
93 uint8_t input_buf[MAX_BUF];
94
95 int dir;
96 int int_enable;
97 int ack; /* last ack state */
98 int tip; /* transfer in progress */
99 };
100
101 #define DIR_INPUT 0
102 #define DIR_OUTPUT 1
103
104 #define BUFB_nINTR 0x08
105 #define BUFB_ACK 0x10
106 #define BUFB_nTIP 0x20
107 #define IFR_SR 0x04
108 #define IFR_ANY 0x80
109 #define ACR_SR_OUT 0x10
110
111
112
113 DEVICE_TICK(adb)
114 {
115 struct adb_data *d = extra;
116 int assert;
117
118 assert = d->reg[vIFR >> VIA_REG_SHIFT] & IFR_ANY;
119 if (assert == IFR_ANY && d->int_enable)
120 assert = 1;
121
122 if (assert)
123 INTERRUPT_ASSERT(d->irq);
124 else if (d->int_asserted)
125 INTERRUPT_DEASSERT(d->irq);
126
127 d->int_asserted = assert;
128 }
129
130
131 /*
132 * adb_reset():
133 *
134 * Reset registers to default values.
135 */
136 static void adb_reset(struct adb_data *d)
137 {
138 d->kbd_dev = 2;
139
140 memset(d->reg, 0, sizeof(d->reg));
141 d->reg[vBufB >> VIA_REG_SHIFT] = BUFB_nINTR | BUFB_nTIP;
142
143 d->cur_output_offset = 0;
144 memset(d->output_buf, 0, sizeof(d->output_buf));
145
146 d->dir = 0;
147 d->int_enable = 0;
148 d->ack = 0;
149 d->tip = 0;
150 }
151
152
153 /*
154 * adb_process_cmd():
155 *
156 * This function should be called whenever a complete ADB command has been
157 * received.
158 */
159 static void adb_process_cmd(struct cpu *cpu, struct adb_data *d)
160 {
161 int i, reg, dev;
162
163 debug("[ adb: COMMAND:");
164 for (i=0; i<d->cur_output_offset; i++)
165 debug(" %02x", d->output_buf[i]);
166 debug(" ]\n");
167
168 if (d->cur_output_offset < 2) {
169 fatal("[ adb: WEIRD output length: %i ]\n",
170 d->cur_output_offset);
171 exit(1);
172 }
173
174 switch (d->output_buf[0]) {
175
176 case 0: /* ADB commands: */
177 if (d->output_buf[1] == 0x00) {
178 /* Reset. */
179 return;
180 }
181 if ((d->output_buf[1] & 0x0c) == 0x0c) {
182 /* ADBTALK: */
183 reg = d->output_buf[1] & 3;
184 dev = d->output_buf[1] >> 4;
185 fatal("dev=%i reg=%i\n", dev, reg);
186 /* Default values: nothing here */
187 d->input_buf[0] = 0x00;
188 d->input_buf[1] = 0x00;
189 d->input_buf[2] = d->output_buf[1];
190 d->cur_input_length = 3;
191 if (dev == d->kbd_dev) {
192 /* Keyboard. */
193 d->input_buf[0] = 0x01;
194 d->input_buf[1] = 0x01;
195 d->input_buf[2] = d->output_buf[1];
196 d->input_buf[3] = 0x01;
197 d->input_buf[4] = 0x01;
198 d->cur_input_length = 5;
199 }
200 } else if ((d->output_buf[1] & 0x0c) == 0x08) {
201 int new_dev_pos = d->output_buf[2] & 15;
202 /* ADBLISTEN: */
203 if ((d->output_buf[1] >> 4) != d->kbd_dev) {
204 fatal("[ adb: ADBLISTEN not to kbd ]\n");
205 exit(1);
206 }
207 if (d->output_buf[3] != 0xfe ||
208 (d->output_buf[2] & 0xf0) != 0x60) {
209 fatal("[ adb: unknown ADBLISTEN ]\n");
210 exit(1);
211 }
212 /* Move device. */
213 d->kbd_dev = new_dev_pos;
214 } else {
215 fatal("[ adb: unknown ADB command? ]\n");
216 exit(1);
217 }
218 break;
219
220 case 1: /* PRAM/RTC: */
221 if (d->cur_output_offset == 3 &&
222 d->output_buf[1] == 0x01 &&
223 d->output_buf[2] == 0x01) {
224 /* Autopoll: */
225 d->input_buf[0] = 0x00;
226 d->input_buf[1] = 0x00;
227 d->input_buf[2] = d->output_buf[1];
228 d->cur_input_length = 3;
229 } else if (d->cur_output_offset == 2 &&
230 d->output_buf[1] == 0x03) {
231 /* Read RTC date/time: */
232 struct timeval tv;
233 gettimeofday(&tv, NULL);
234 d->input_buf[0] = tv.tv_sec >> 24;
235 d->input_buf[1] = tv.tv_sec >> 16;
236 d->input_buf[2] = tv.tv_sec >> 8;
237 d->input_buf[3] = tv.tv_sec;
238 d->cur_input_length = 4;
239 } else if (d->cur_output_offset == 2 &&
240 d->output_buf[1] == 0x11) {
241 /* Reboot. */
242 fatal("[ adb: reboot. TODO: make this nicer ]\n");
243 exit(1);
244 } else {
245 fatal("[ adb: UNIMPLEMENTED PRAM/RTC command ]\n");
246 exit(1);
247 }
248 break;
249
250 default:fatal("[ adb: UNKNOWN command type 0x%02x ]\n",
251 d->output_buf[0]);
252 exit(1);
253 }
254
255 d->reg[vBufB >> VIA_REG_SHIFT] &= ~BUFB_nINTR;
256 d->reg[vIFR >> VIA_REG_SHIFT] |= IFR_ANY | IFR_SR;
257 d->reg[vSR >> VIA_REG_SHIFT] = 0x00; /* Dummy. */
258 }
259
260
261 /*
262 * adb_transfer():
263 *
264 * This function should be called whenever a new transfer is started, a
265 * transfer is finished, or when the next byte in a transfer should be
266 * sent/received.
267 */
268 static void adb_transfer(struct cpu *cpu, struct adb_data *d, int state_change)
269 {
270 unsigned char c = 0x00;
271
272 if (state_change) {
273 if (d->tip == 0) {
274 debug("[ adb: transfer #%lli done ]\n",
275 (long long)d->transfer_nr);
276 if (d->cur_output_offset > 0)
277 adb_process_cmd(cpu, d);
278 d->transfer_nr ++;
279 return;
280 }
281 debug("[ adb: starting transfer #%lli: %s ]\n", (long long)
282 d->transfer_nr, d->dir == DIR_INPUT? "INPUT" : "OUTPUT");
283 d->cur_input_offset = d->cur_output_offset = 0;
284 }
285
286 debug("[ adb: transfer #%lli: ", (long long)d->transfer_nr);
287
288 switch (d->dir) {
289
290 case DIR_INPUT:
291 if (d->cur_input_offset >= d->cur_input_length)
292 fatal("[ adb: INPUT beyond end of data? ]\n");
293 else
294 c = d->input_buf[d->cur_input_offset ++];
295 debug("input 0x%02x", c);
296 d->reg[vSR >> VIA_REG_SHIFT] = c;
297 d->reg[vIFR >> VIA_REG_SHIFT] |= IFR_ANY | IFR_SR;
298 if (d->cur_input_offset >= d->cur_input_length)
299 d->reg[vBufB >> VIA_REG_SHIFT] |= BUFB_nINTR;
300 break;
301
302 case DIR_OUTPUT:
303 c = d->reg[vSR >> VIA_REG_SHIFT];
304 debug("output 0x%02x", c);
305 d->reg[vIFR >> VIA_REG_SHIFT] |= IFR_ANY | IFR_SR;
306 d->reg[vBufB >> VIA_REG_SHIFT] |= BUFB_nINTR;
307 d->output_buf[d->cur_output_offset ++] = c;
308 break;
309 }
310
311 debug(" ]\n");
312 }
313
314
315 DEVICE_ACCESS(adb)
316 {
317 uint64_t idata = 0, odata = 0;
318 struct adb_data *d = extra;
319 uint8_t old = 0;
320
321 if (writeflag == MEM_WRITE)
322 idata = memory_readmax64(cpu, data, len);
323
324 #ifdef ADB_DEBUG
325 if ((relative_addr & ((1 << VIA_REG_SHIFT) - 1)) != 0)
326 fatal("[ adb: %s non-via register? offset 0x%x ]\n",
327 writeflag == MEM_READ? "read from" : "write to",
328 (int)relative_addr);
329 else if (writeflag == MEM_READ)
330 fatal("[ adb: read from %s: 0x%02x ]\n",
331 via_regname[relative_addr >> VIA_REG_SHIFT],
332 (int)d->reg[relative_addr >> VIA_REG_SHIFT]);
333 else
334 fatal("[ adb: write to %s: 0x%02x ]\n", via_regname[
335 relative_addr >> VIA_REG_SHIFT], (int)idata);
336 #endif
337
338 if (writeflag == MEM_READ)
339 odata = d->reg[relative_addr >> VIA_REG_SHIFT];
340 else {
341 old = d->reg[relative_addr >> VIA_REG_SHIFT];
342 switch (relative_addr) {
343 case vIFR:
344 /*
345 * vIFR is write-ones-to-clear, and the highest bit
346 * (IFR_ANY) is set if any of the lower bits are set.
347 */
348 d->reg[relative_addr >> VIA_REG_SHIFT] &= ~(idata|0x80);
349 if (d->reg[relative_addr >> VIA_REG_SHIFT] & 0x7f)
350 d->reg[relative_addr >> VIA_REG_SHIFT] |= 0x80;
351 break;
352 default:
353 d->reg[relative_addr >> VIA_REG_SHIFT] = idata;
354 }
355 }
356
357 switch (relative_addr) {
358
359 case vBufB:
360 /* OK */
361 if (writeflag == MEM_WRITE) {
362 int old_tip = d->tip;
363 int old_ack = d->ack;
364 if (idata & BUFB_nINTR)
365 idata &= ~BUFB_nINTR;
366 d->ack = 0;
367 if (idata & BUFB_ACK) {
368 idata &= ~BUFB_ACK;
369 d->ack = 1;
370 }
371 d->tip = 1;
372 if (idata & BUFB_nTIP) {
373 idata &= ~BUFB_nTIP;
374 d->tip = 0;
375 }
376 if (idata != 0)
377 fatal("[ adb: WARNING! UNIMPLEMENTED bits in"
378 " vBufB: 0x%02x ]\n", (int)idata);
379 if (old_tip != d->tip)
380 adb_transfer(cpu, d, 1);
381 else if (old_ack != d->ack)
382 adb_transfer(cpu, d, 0);
383 }
384 break;
385
386 case vDirB:
387 break;
388
389 case vSR:
390 /* Clear the SR interrupt flag, if set: */
391 d->reg[vIFR >> VIA_REG_SHIFT] &= ~IFR_SR;
392 break;
393
394 case vACR:
395 /* OK */
396 if (writeflag == MEM_WRITE) {
397 if (idata & ACR_SR_OUT)
398 d->dir = DIR_OUTPUT;
399 else
400 d->dir = DIR_INPUT;
401 }
402 break;
403
404 case vIFR:
405 /* OK */
406 break;
407
408 case vIER:
409 /* OK */
410 if (writeflag == MEM_WRITE) {
411 d->int_enable = idata & 0x80? 1 : 0;
412 if (idata != 0x04 && idata != 0x84)
413 fatal("[ adb: WARNING! vIER value 0x%x is"
414 " UNKNOWN ]\n", (int)idata);
415 }
416 break;
417
418 default:if ((relative_addr & ((1 << VIA_REG_SHIFT) - 1)) != 0)
419 fatal("[ adb: %s non-via register? offset 0x%x ]\n",
420 writeflag == MEM_READ? "read from" : "write to",
421 (int)relative_addr);
422 else if (writeflag == MEM_READ)
423 fatal("[ adb: READ from UNIMPLEMENTED %s ]\n",
424 via_regname[relative_addr >> VIA_REG_SHIFT]);
425 else
426 fatal("[ adb: WRITE to UNIMPLEMENTED %s: 0x%x ]\n",
427 via_regname[relative_addr >> VIA_REG_SHIFT],
428 (int)idata);
429 exit(1);
430 }
431
432 if (writeflag == MEM_READ)
433 memory_writemax64(cpu, data, len, odata);
434
435 return 1;
436 }
437
438
439 DEVINIT(adb)
440 {
441 struct adb_data *d = malloc(sizeof(struct adb_data));
442
443 if (d == NULL) {
444 fprintf(stderr, "out of memory\n");
445 exit(1);
446 }
447 memset(d, 0, sizeof(struct adb_data));
448
449 INTERRUPT_CONNECT(devinit->interrupt_path, d->irq);
450
451 adb_reset(d);
452
453 memory_device_register(devinit->machine->memory, devinit->name,
454 devinit->addr, DEV_ADB_LENGTH, dev_adb_access, d, DM_DEFAULT, NULL);
455 machine_add_tickfunction(devinit->machine, dev_adb_tick, d,
456 TICK_SHIFT, 0.0);
457
458 return 1;
459 }
460

  ViewVC Help
Powered by ViewVC 1.1.26