/[gxemul]/upstream/0.3.8/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

Annotation of /upstream/0.3.8/src/devices/dev_adb.c

Parent Directory Parent Directory | Revision Log Revision Log


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

  ViewVC Help
Powered by ViewVC 1.1.26