From: vanepp@fraser.sfu.ca (Peter Van Epp)
Subject: Re: Programming the 8259 (and a possible 386bsd bug)
Organization: Simon Fraser University, Burnaby, B.C., Canada
Date: Wed, 27 Jan 1993 03:29:47 GMT

dmuntz@quip.eecs.umich.edu (Dan Muntz) writes:

>I'd like to know what people have found to be good (accurate/complete) sources
>of information about programming the 8259 as it is used in 386/486 machines.

The chip data sheet while less than readable, and not necessarily complete 
(nor accurate) at least gives the meaning of all the bits and registers
and what they in theory do.

>In isa.c in 386bsd the following sequence is used to initialize the "lower"
>8259:

From the data sheet on the 8259a:

>	/* initialize 8259's */
>	outb(IO_ICU1, 0x11);            /* reset; program device, four bytes */

Icw1:  (address line is 0)

bits d7 - d5 Interrupt vector address (bits 7 - 3, bits 2 - 0 are set to the  
			level of the interrupt) (0)
bit  d4      1 (always)
bit  d3      edge trigger mode (0), a 1 would be level trigger mode
bit  d2	     call interval of 8 (0), a 1 would be call interval of 4
		but it is ignored in 8086 mode (which this is, see later)
bit  d1      cascade mode (0), a 1 would be single mode (no icw3)
bit  d0	     Icw4 needed (1) no Icw4 needed would be 0. 

>	outb(IO_ICU1+1, NRSVIDT);       /* starting at this vector index */

Icw2:  (address line is one)

Interrupt vector address (the bits are probably not important).

>	outb(IO_ICU1+1, 1<<2);          /* slave on line 2 */

Icw3:  (address line is one)

bits d7 - d3  0 (always)

bits d2 - d0  binary address of slave id (2 in this case).

>	outb(IO_ICU1+1, 1);             /* 8086 mode */

Icw4:  (address line is 1)

bits d7 - d5  0 (always)
bit d4        not fully special nested mode (0) 1 is fully special nested mode
bit d3 - d2   0x non buffered mode 
	      10 buffered mode slave
	      11 buffered mode master
bit d1        normal EOI (0) 1 auto EOI (EOI = End Of Interrupt)
bit d0	      8086 mode (1) 0 8080 mode (my first micro, the CPU chip was $350,
			and 4k of static ram was $500, add wire wrap sockets,
			ttl, an lh0026 MOS clock driver and 8 toggle switches
			and voila heaven!).

This completes the initialization sequence, now for the interrupt masks.

>	outb(IO_ICU1+1, 0xff);		/* leave interrupts masked */

Ocw1: (address line is 1)

bits d7 - d0   corresponding interrupt is masked (1) or not masked (0)

>	outb(IO_ICU1, 2);		/* default to ISR on read */

Ocw2: (address line is 0)

bit  d7       non rotating priority (0), 1 is rotating priority
bit  d6       no action (0), 1 is specific EOI (selected by bits 2 - 0)
bit  d5	      no action (0), 1 is reset highest active interrupt
bits d4 - d3  0 (always)
bit  d2 - d0  interrupt level to reset (level 2 in this case, but probably 
		don't care?).

I expect that you are correct, the comment is wrong. When something needs
doing, Ocw3 will get written to do whatever wants to be done (and then
indeed, writing 3 not 2 will get you the ISR register). 

It is now sitting waiting for an OCW3 command whose format is

Ocw3: (address line is 0)

bit d7         don't care
bit d6 - d5    00 no action
	       01 no action
	       10 read special mask          
	       11 set special mask
bit d4	       0 (always)
bit d3         1 (always)
bit d2         0 no action, 1 read code of highest int on next read. 
bits d1 - d0   00 no action
	       01 no action
	       10 read IR reg on next read pulse (IR = Interrupt Request reg)
			(bit map of incoming interrupts, not masked)
	       11 read IS reg on next rd pulse (IS = In Service reg) 
			(shows the priority of the levels being serviced).

A quick grep for IO_ICU finds hits in icu.h, a look at that finds a bunch of
inline assembler in macros, at least some of which deals with doing EOIs to
interrupt controllers. I expect that you will find the missing Ocw3 hidden
away in there somewhere.

>According to the meager amount of information I've found, the last line
>should be outb(IO_ICU1, 3); to select ISR on read (or the comment should
>say IRR instead of ISR).  From other points in the code, it appears that
>ISR is expected.  If this line is wrong, the other 8259 is also being
>initialized incorrectly.  Can anyone shed some light on this?

Hopefully the above helps some, if you need more info just ask and I'll
see what else I can dig up (or maybe someone more knowledgable will comment),
I seem to remember some comments on $%$#$%^ interrupt controllers in one
of the early DDJ articles on BSD386 as well, so that may be another source
of info (straight from the horse's mouth as it were!).

Peter Van Epp / Operations and Technical Support 
Simon Fraser University, Burnaby, B.C. Canada
Newsgroups: comp.unix.bsd,alt.sources