Discussion:
8259 PIC special mask mode
(too old to reply)
James Harris
2021-04-14 10:31:16 UTC
Permalink
Anyone up for a highly technical discussion? :-)

Is there any way with 8259s to implement custom interrupt priorities?

One could consider the standard priority order as

0 - PIT
1 - Keyboard
8...15 RTC and rest of slave PIC
3 - Serial port
4 - Serial port
5...7 Rest of master PIC

I'd prefer something like

0 - PIT
8 - RTC
3 - Serial port
4 - Serial port
Everything else

In other words, timers then serial ports then the rest.

You may prefer a different order and we've discussed this in the past
but AFAICR we've not found a way to achieve it.

That's where special mask mode comes in. I've read the description of it
in the datasheet before but never understood it. I still don't even now.
:-( But on reading it again it sounds as though it /might/ be usable for
such custom prioritisation.

The Intel 8259A datasheet says this about it:

"Special Mask Mode

"Some applications may require an interrupt service routine to
dynamically alter the system priority structure during its execution
under software control. For example, the routine may wish to inhibit
lower priority requests for a portion of its execution but enable some
of them for another portion.

"The difficulty here is that if an Interrupt Request is acknowledged and
an End of Interrupt command did not reset its IS bit (i.e., while
executing a service routine), the 8259A would have inhibited all lower
priority requests with no easy way for the routine to enable them.

"That is where the Special Mask Mode comes in. In the special Mask Mode,
when a mask bit is set in OCW1, it inhibits further interrupts at that
level and enables interrupts from all other levels (lower as well as
higher) that are not masked. Thus, any interrupts may be selectively
enabled by loading the mask register."

See what I mean? It sounds as thought it might allow custom priorities
(and more).

How does it look to you?
--
James Harris
Scott Lurndal
2021-04-14 14:24:06 UTC
Permalink
Post by James Harris
Anyone up for a highly technical discussion? :-)
Is there any way with 8259s to implement custom interrupt priorities?
One could consider the standard priority order as
0 - PIT
1 - Keyboard
8...15 RTC and rest of slave PIC
3 - Serial port
4 - Serial port
5...7 Rest of master PIC
I'd prefer something like
0 - PIT
8 - RTC
3 - Serial port
4 - Serial port
Everything else
In other words, timers then serial ports then the rest.
You may prefer a different order and we've discussed this in the past
but AFAICR we've not found a way to achieve it.
That's where special mask mode comes in. I've read the description of it
in the datasheet before but never understood it. I still don't even now.
:-( But on reading it again it sounds as though it /might/ be usable for
such custom prioritisation.
"Special Mask Mode
"Some applications may require an interrupt service routine to
dynamically alter the system priority structure during its execution
under software control. For example, the routine may wish to inhibit
lower priority requests for a portion of its execution but enable some
of them for another portion.
"The difficulty here is that if an Interrupt Request is acknowledged and
an End of Interrupt command did not reset its IS bit (i.e., while
executing a service routine), the 8259A would have inhibited all lower
priority requests with no easy way for the routine to enable them.
"That is where the Special Mask Mode comes in. In the special Mask Mode,
when a mask bit is set in OCW1, it inhibits further interrupts at that
level and enables interrupts from all other levels (lower as well as
higher) that are not masked. Thus, any interrupts may be selectively
enabled by loading the mask register."
See what I mean? It sounds as thought it might allow custom priorities
(and more).
How does it look to you?
The mask bit doesn't alter the priorities. It does mask out
individual priorities so they are not considered when calculating
the highest priority interrupt.
wolfgang kern
2021-04-14 20:23:24 UTC
Permalink
Post by James Harris
Anyone up for a highly technical discussion? :-)
Is there any way with 8259s to implement custom interrupt priorities?
One could consider the standard priority order as
 0 - PIT
 1 - Keyboard
 8...15 RTC and rest of slave PIC
 3 - Serial port
 4 - Serial port
 5...7 Rest of master PIC
I'd prefer something like
 0 - PIT
 8 - RTC
 3 - Serial port
 4 - Serial port
 Everything else
In other words, timers then serial ports then the rest.
moving the KBD to lower priority could cause annoying delayed response
(the user has to wait for serial packets completion).
RTCL-I/O is pretty slow but must be read to avoid locking.
[...]
Post by James Harris
"That is where the Special Mask Mode comes in. In the special Mask Mode,
when a mask bit is set in OCW1, it inhibits further interrupts at that
level and enables interrupts from all other levels (lower as well as
higher) that are not masked. Thus, any interrupts may be selectively
enabled by loading the mask register."
See what I mean? It sounds as thought it might allow custom priorities
(and more).
How does it look to you?
it is a very bad idea to intentionally risk lose of an hardware event.
I made the IRQ-routines as short as possible, apart from "handlers" and
reenable IRQs right after the hardware is satisfied (ie by I/O read).
__
wolfgang
James Harris
2021-04-15 09:22:24 UTC
Permalink
Post by wolfgang kern
Post by James Harris
Anyone up for a highly technical discussion? :-)
Is there any way with 8259s to implement custom interrupt priorities?
One could consider the standard priority order as
  0 - PIT
  1 - Keyboard
  8...15 RTC and rest of slave PIC
  3 - Serial port
  4 - Serial port
  5...7 Rest of master PIC
I'd prefer something like
  0 - PIT
  8 - RTC
  3 - Serial port
  4 - Serial port
  Everything else
In other words, timers then serial ports then the rest.
moving the KBD to lower priority could cause annoying delayed response
(the user has to wait for serial packets completion).
If you prefer then the KBC could be serviced after the timers and before
the serial ports. But that's so only if we can implement custom
prioritisation. Hence this thread.
Post by wolfgang kern
RTCL-I/O is pretty slow but must be read to avoid locking.
You say it's slow but how slow is it compared with, say, the KBC or a
serial port?

I guess any timings will depend on the specific machine. Some machines
may even deal with certain port reads via traps to software and be
markedly slower.

That said, even though the CMOS requires two IO cycles (write address,
access port) it's normally only read once per second, isn't it?
Post by wolfgang kern
[...]
Post by James Harris
"That is where the Special Mask Mode comes in. In the special Mask
Mode, when a mask bit is set in OCW1, it inhibits further interrupts
at that level and enables interrupts from all other levels (lower as
well as higher) that are not masked. Thus, any interrupts may be
selectively enabled by loading the mask register."
See what I mean? It sounds as thought it might allow custom priorities
(and more).
How does it look to you?
it is a very bad idea to intentionally risk lose of an hardware event.
The point of prioritising the serial ports is to avoid losing data. AISI
even at 57,600 baud four serial ports could generate something like
40,000 interrupts per second. I've not tested whether various machines
could handle so many but to me that sounds like a lot!

Modern 16550A (note the A) chips have small FIFOs but the FIFO in the
16550 was apparently broken. And I think the 16550 can go up to
something like 115k baud. So you could be looking at an even higher
interrupt rate. Hence the need for rapid service of interrupts from
serial ports.
Post by wolfgang kern
I made the IRQ-routines as short as possible, apart from "handlers" and
reenable IRQs right after the hardware is satisfied (ie by I/O read).
Do you issue EOI at the start of interrupt handling or at the end? If
at the end then while processing one interrupt - say that of a hard
drive - all lower-priority interrupts will be kept waiting. And with the
default priority order that would include serial ports. Not good, I
suspect. :-(
--
James Harris
wolfgang kern
2021-04-15 14:35:14 UTC
Permalink
Post by James Harris
Post by wolfgang kern
Post by James Harris
Anyone up for a highly technical discussion? :-)
Is there any way with 8259s to implement custom interrupt priorities?
One could consider the standard priority order as
  0 - PIT
  1 - Keyboard
  8...15 RTC and rest of slave PIC
  3 - Serial port
  4 - Serial port
  5...7 Rest of master PIC
I'd prefer something like
  0 - PIT
  8 - RTC
  3 - Serial port
  4 - Serial port
  Everything else
In other words, timers then serial ports then the rest.
moving the KBD to lower priority could cause annoying delayed response
(the user has to wait for serial packets completion).
If you prefer then the KBC could be serviced after the timers and before
the serial ports. But that's so only if we can implement custom
prioritisation. Hence this thread.
Post by wolfgang kern
RTCL-I/O is pretty slow but must be read to avoid locking.
You say it's slow but how slow is it compared with, say, the KBC or a
serial port?
I guess any timings will depend on the specific machine. Some machines
may even deal with certain port reads via traps to software and be
markedly slower.
That said, even though the CMOS requires two IO cycles (write address,
access port) it's normally only read once per second, isn't it?
Post by wolfgang kern
[...]
Post by James Harris
"That is where the Special Mask Mode comes in. In the special Mask
Mode, when a mask bit is set in OCW1, it inhibits further interrupts
at that level and enables interrupts from all other levels (lower as
well as higher) that are not masked. Thus, any interrupts may be
selectively enabled by loading the mask register."
See what I mean? It sounds as thought it might allow custom
priorities (and more).
How does it look to you?
it is a very bad idea to intentionally risk lose of an hardware event.
The point of prioritising the serial ports is to avoid losing data. AISI
even at 57,600 baud four serial ports could generate something like
40,000 interrupts per second. I've not tested whether various machines
could handle so many but to me that sounds like a lot!
Modern 16550A (note the A) chips have small FIFOs but the FIFO in the
16550 was apparently broken. And I think the 16550 can go up to
something like 115k baud. So you could be looking at an even higher
interrupt rate. Hence the need for rapid service of interrupts from
serial ports.
I almost forgot about 16550(with and w/o A). They aren't there anymore.
today (since 1998) serial devices are USB things with larger buffers.
So the interrupt frequency is quite lesser than with 16 byte FIFOs.
Post by James Harris
Post by wolfgang kern
I made the IRQ-routines as short as possible, apart from "handlers" and
reenable IRQs right after the hardware is satisfied (ie by I/O read).
Do you issue EOI at the start of interrupt handling or at the end?  If
at the end then while processing one interrupt - say that of a hard
drive - all lower-priority interrupts will be kept waiting. And with the
default priority order that would include serial ports. Not good, I
suspect. :-(
No, I treat all IRQs equal and non have to wait for another to complete.
this allow user interception of long lasting events.

just an example of my IRQ_1 and IRQ_12 routines:
(it actually does a few things more)
push DS
push -1
push ax
pop DS ;-1 is were my vars reside [FFFF:xxxx... aka HMA]

IN al,[60]
STI ;allow any other IRQ already here

mov bx,[cur_kbd] ;reset by handler after key press.release recognized
or [events],02 ;cleared by handler after action taken
cmp bx,8
jnc ignore ;occurs only if the handler failed
inc [cur_kbd]
mov [bx+kbdbuf],al
ignore:
mov al,20
out [20],al ;EOI
pop ax
pop DS
iret

So all my handlers are part of the main idle MUX loop and the OS can
decide which to handle first or just ignore (ie unwanted mice moves).
__
wolfgang
James Harris
2021-04-16 08:53:15 UTC
Permalink
...
Post by wolfgang kern
Post by James Harris
Do you issue EOI at the start of interrupt handling or at the end?  If
at the end then while processing one interrupt - say that of a hard
drive - all lower-priority interrupts will be kept waiting. And with
the default priority order that would include serial ports. Not good,
I suspect. :-(
No, I treat all IRQs equal and non have to wait for another to complete.
this allow user interception of long lasting events.
I'm a bit confused. I think you are saying that interrupts don't have to
wait for others to complete. However, in the code below you send the EOI
at the end. Won't that mean that while the routine runs any lower
priority interrupts will be inhibited? What I want to do is to choose a
custom ordering if multiple interrupts are signalled at the same time.
Post by wolfgang kern
(it actually does a few things more)
I take the code as it stands as being an outline for irq1 since it EOIs
just the master PIC.
Post by wolfgang kern
push DS
push -1
push ax
I presume those last two should be the other way round and that you save
BX somewhere.

Speaking of which I think I may have a solution to my initial query of
how to impose custom priorities. Am checking it over and debating with
myself whether to post it to this thread or a new one but I think that
you, in particular, may approve of the way it only pushes registers
which it needs to. On the other hand there is something about it which I
think you may not like. But we'll see.
Post by wolfgang kern
pop DS              ;-1 is were my vars reside [FFFF:xxxx... aka HMA]
IN al,[60]
STI                 ;allow any other IRQ already here
mov bx,[cur_kbd]    ;reset by handler after key press.release recognized
or [events],02      ;cleared by handler after action taken
cmp bx,8
jnc ignore          ;occurs only if the handler failed
I like it. Only a hex programmer would write JNC instead of JAE here. :-)
Post by wolfgang kern
inc [cur_kbd]
mov [bx+kbdbuf],al
mov al,20
out [20],al         ;EOI
pop ax
pop DS
iret
So all my handlers are part of the main idle MUX loop and the OS can
decide which to handle first or just ignore (ie unwanted mice moves).
I take it by that that you mean the actual handling of bytes in that
small (tiny!) buffer would be done by a task rather than during
interrupt time. That's cool as long as the buffer is large enough and
the keyboard handler task has sufficient priority.

And on that, while I don't know which scan code set you are using, if
using set 2 some keys can generate long codes. According to


https://web.archive.org/web/20001202114700/http://www.execpc.com/~geezer/os/kbd.txt

Pause can emit an 8-byte code all by itself:

E11477E1F014F077

and a quick bounce of the Right cursor key can generate

E012E074 E0F074E0F012

That's a 4-byte make code and a 6-byte break code.
--
James Harris
wolfgang kern
2021-04-16 11:45:07 UTC
Permalink
Post by James Harris
...
Post by wolfgang kern
Post by James Harris
Do you issue EOI at the start of interrupt handling or at the end?
If at the end then while processing one interrupt - say that of a
hard drive - all lower-priority interrupts will be kept waiting. And
with the default priority order that would include serial ports. Not
good, I suspect. :-(
No, I treat all IRQs equal and non have to wait for another to complete.
this allow user interception of long lasting events.
I'm a bit confused. I think you are saying that interrupts don't have to
wait for others to complete. However, in the code below you send the EOI
at the end. Won't that mean that while the routine runs any lower
priority interrupts will be inhibited? What I want to do is to choose a
custom ordering if multiple interrupts are signalled at the same time.
inhibited only if rotating priority is selected.
remember our discussion of PIT programming ? the timer is the only
level-triggered IRQ, so I let it interrupt all other.
Post by James Harris
I take the code as it stands as being an outline for irq1 since it EOIs
just the master PIC.
yeah but the IRQ12 code looks almost equal.
Post by James Harris
Post by wolfgang kern
push DS
push -1
push ax
I presume those last two should be the other way round and that you save
BX somewhere.
yes, I typed it out of my head. the story contains much more functions.
Post by James Harris
Speaking of which I think I may have a solution to my initial query of
how to impose custom priorities. Am checking it over and debating with
myself whether to post it to this thread or a new one but I think that
you, in particular, may approve of the way it only pushes registers
which it needs to. On the other hand there is something about it which I
think you may not like. But we'll see.
Post by wolfgang kern
pop DS              ;-1 is were my vars reside [FFFF:xxxx... aka HMA]
IN al,[60]
STI                 ;allow any other IRQ already here
mov bx,[cur_kbd]    ;reset by handler after key press.release recognized
or [events],02      ;cleared by handler after action taken
cmp bx,8
jnc ignore          ;occurs only if the handler failed
I like it. Only a hex programmer would write JNC instead of JAE here. :-)
Post by wolfgang kern
inc [cur_kbd]
mov [bx+kbdbuf],al
mov al,20
out [20],al         ;EOI
pop ax
pop DS
iret
So all my handlers are part of the main idle MUX loop and the OS can
decide which to handle first or just ignore (ie unwanted mice moves).
I take it by that that you mean the actual handling of bytes in that
small (tiny!) buffer would be done by a task rather than during
interrupt time. That's cool as long as the buffer is large enough and
the keyboard handler task has sufficient priority.
And on that, while I don't know which scan code set you are using, if
using set 2 some keys can generate long codes. According to
https://web.archive.org/web/20001202114700/http://www.execpc.com/~geezer/os/kbd.txt
thanks I have it (dated 2000)
Post by James Harris
 E11477 E1F014 F077
I use set1 so my Pause-Key produces only 6 bytes.
Post by James Harris
and a quick bounce of the Right cursor key can generate
 E012E074  E0F074E0 F012
my IRQ-input is about 10e5 times faster than any bouncing keys :)
Post by James Harris
That's a 4-byte make code and a 6-byte break code.
YES, that's why my KBD-buffer is 8 bytes "large".
a make set my MAKE-EVENT-flag and the break another flag.

while the IRQ routine is short but is invoked by every byte it allows to
be interrupted by IRQ_0. BUT running tasks wont be interrupted until a
complete packet [key or mouse or serial or HD-read] arrived.
The scheduler can then decide to intercept any running task.
__
wolfgang
by some luck I still stock a few cherry PS/2 keyboards, I like them.
Loading...