--- trunk/src/devices/dev_8259.c 2007/10/08 16:18:31 11 +++ trunk/src/devices/dev_8259.c 2007/10/08 16:18:38 12 @@ -25,11 +25,11 @@ * SUCH DAMAGE. * * - * $Id: dev_8259.c,v 1.10 2005/06/20 05:52:48 debug Exp $ + * $Id: dev_8259.c,v 1.14 2005/08/05 09:08:20 debug Exp $ * * 8259 Programmable Interrupt Controller. * - * This is mostly bogus. TODO. See the following URL for more details: + * See the following URL for more details: * http://www.nondot.org/sabre/os/files/MiscHW/8259pic.txt */ @@ -48,6 +48,8 @@ #define DEV_8259_LENGTH 2 +/* #define DEV_8259_DEBUG */ + /* * dev_8259_access(): @@ -58,9 +60,18 @@ { struct pic8259_data *d = (struct pic8259_data *) extra; uint64_t idata = 0, odata = 0; + int i; idata = memory_readmax64(cpu, data, len); +#ifdef DEV_8259_DEBUG + if (writeflag == MEM_READ) + fatal("[ 8259: read from 0x%x ]\n", (int)relative_addr); + else + fatal("[ 8259: write to 0x%x: 0x%x ]\n", + (int)relative_addr, (int)idata); +#endif + switch (relative_addr) { case 0x00: if (writeflag == MEM_WRITE) { @@ -73,9 +84,10 @@ fatal("[ 8259: WARNING: Bit 2 set ]\n"); /* Bit 1: 0=cascade, 1=single */ /* Bit 0: 1=4th init byte */ - if (!(idata & 0x01)) + /* This happens on non-x86 systems: + if (!(idata & 0x01)) fatal("[ 8259: WARNING: Bit 0 NOT set!" - "!! ]\n"); + "!! ]\n"); */ d->init_state = 1; break; } @@ -94,12 +106,26 @@ case 0x0b: d->current_command = 0x0b; break; + case 0x0c: + /* Put Master in Buffered Mode */ + d->current_command = 0x0c; + break; case 0x20: /* End Of Interrupt */ + /* + * TODO: in buffered mode, is this an EOI 0? + */ d->irr &= ~d->isr; d->isr = 0; /* Recalculate interrupt assertions: */ - cpu_interrupt(cpu, 16); + cpu_interrupt(cpu, d->irq_nr); break; + case 0x21: /* Specific EOI */ + case 0x22: + case 0x23: + case 0x24: + case 0x25: + case 0x26: + case 0x27: case 0x60: case 0x61: case 0x62: @@ -111,7 +137,7 @@ d->irr &= ~(1 << (idata & 7)); d->isr &= ~(1 << (idata & 7)); /* Recalculate interrupt assertions: */ - cpu_interrupt(cpu, 16); + cpu_interrupt(cpu, d->irq_nr); break; case 0x68: /* Set Special Mask Mode */ /* TODO */ @@ -139,10 +165,28 @@ case 0x0b: odata = d->isr; break; + case 0x0c: + /* Buffered mode. */ default: - fatal("[ 8259: unimplemented command 0x%02x" - " while reading ]\n", d->current_command); - cpu->running = 0; + odata = 0x00; + for (i=0; i<8; i++) + if ((d->irr >> i) & 1) { + odata = 0x80 | i; + break; + } + break; + /* + * TODO: The "default" label should really do + * something like this: + * + * fatal("[ 8259: unimplemented command 0x%02x" + * " while reading ]\n", d->current_command); + * cpu->running = 0; + * + * but Linux seems to read from the secondary PIC + * in a manner which works better the way things + * are coded right now. + */ } } break; @@ -150,10 +194,11 @@ if (d->init_state > 0) { if (d->init_state == 1) { d->irq_base = idata & 0xf8; - if (idata & 7) + /* This happens on non-x86 machines: + if (idata & 7) fatal("[ 8259: WARNING! Lowest" " bits in Init Cmd 1 are" - " non-zero! ]\n"); + " non-zero! ]\n"); */ d->init_state = 2; } else if (d->init_state == 2) { /* Slave attachment. TODO */ @@ -174,7 +219,7 @@ if (writeflag == MEM_WRITE) { d->ier = idata; /* Recalculate interrupt assertions: */ - cpu_interrupt(cpu, 16); + cpu_interrupt(cpu, d->irq_nr); } else { odata = d->ier; } @@ -200,12 +245,22 @@ /* * devinit_8259(): + * + * Initialize an 8259 PIC. Important notes: + * + * x) Most systems use _TWO_ 8259 PICs. These should be registered + * as separate devices. + * + * x) The irq number specified is the number used to re-calculate + * CPU interrupt assertions. It is _not_ the irq number at + * which the PIC is connected. (That is left to machine specific + * code in src/machine.c.) */ int devinit_8259(struct devinit *devinit) { struct pic8259_data *d = malloc(sizeof(struct pic8259_data)); char *name2; - int nlen = 40; + size_t nlen = strlen(devinit->name) + 20; if (d == NULL) { fprintf(stderr, "out of memory\n"); @@ -216,11 +271,13 @@ name2 = malloc(nlen); snprintf(name2, nlen, "%s", devinit->name); - if ((devinit->addr & 0xfff) == 0xa0) + if ((devinit->addr & 0xfff) == 0xa0) { strlcat(name2, " [secondary]", nlen); + d->irq_base = 8; + } memory_device_register(devinit->machine->memory, name2, - devinit->addr, DEV_8259_LENGTH, dev_8259_access, (void *)d, + devinit->addr, DEV_8259_LENGTH, dev_8259_access, d, MEM_DEFAULT, NULL); devinit->return_ptr = d;