All of the PIC handling code is in the "PIC" subdirectory.
These are the functions defined in base/dev/pic/pic.c.
This is the pic debug message printer. It writes out some basic information, followed by an informative message. The basic information consists of: interrupt nesting counter change flag (+, -, or blank) interrupt nesting count (pic_icount) interrupt level change flag (+, -, or blank) current interrupt level interrupt in-service register interrupt mask register interrupt request register message part one decimal data value message part two
If the message part 2 pointer is a null pointer, then only message part one (without the data value) is printed.
The change flags are there to facilitate grepping for changes in pic_ilevel and pic_icount
To avoid line wrap, the first seven values are printed without labels. Instead, a header line is printed every 15 messages.
write_pic_0() and write_pic1() implement dos writes to the pic ports. They are called by the code that emulates inb and outb instructions. Each function implements both ports for the pic: pic0 is on ports 0x20 and 0x21; pic1 is on ports 0xa0 and 0xa1. These functions take
read_pic0 and read_pic1 return the values for the interrupt mask register (port 1), or either the in service register or interrupt request register, as determined by the last OCW3 command (port 0). These functions take a single parameter, which is a port number (0 or 1). They are called by code that emulates the inb instruction.
pic_seti is used to initialize an interrupt for dosemu. It requires four parameters. The first parameter is the interrupt level, which may select the NMI, any of the IRQs, or any of the 16 extra levels (16 - 31). The second parameter is the dosemu function to be called when the interrupt is activated. This function should call do_irq() if the DOS interrupt is really to be activated. If there is no special dosemu code to call, the second parameter can specify do_irq(), but see that description for some special considerations. The third parameter is a number of an interrupt to activate if there is no default interrupt for this ilevel. The fourth parameter is the dosemu function to be called from do_irq(). Required by some internal dosemu drivers that needs some additional code before calling an actual handler. This function MUST provide a EOI at the end of a callback.
run_irqs, which is initiated via the macro pic_run, is the "brains" of the pic. It is called from the vm86() loop, checks for the highest priority interrupt requested, and executes it. This function is written in assembly language in order to take advantage of atomic (indivisible) instructions, so that it should be safe for a two process model, even in a multiple CPU machine. A c language version was started, but it became impossible, even with in-line assembly macros, because such macros can only return a single result. If I find a way to do it in c, I will, but don't hold your breath.
I found a way to write it in C --EB 15 Jan 97
do_irq() calls the correct do_int(). It then executes a vm86 loop until an outb( end-of-interrupt) is found. For priority levels 0 and >15 (not real IRQs), vm86 executes once, then returns, since no outb20 will come. Returns: 0 = complete, 1 = interrupt not run because it directly calls our "bios" See run_timer_tick() in timer.c for an example To assure notification when the irq completes, we push flags, ip, and cs here and fake cs:ip to PIC_[SEG,OFF], where there is a hlt. This makes the irq generate a sigsegv, which calls pic_iret when it completes. pic_iret then pops the real cs:ip from the stack. This routine is RE-ENTRANT - it calls run_irqs, which may call an interrupt routine, which may call do_irq(). Be Careful! !!!!!!!!!!!!!!!!!! No single interrupt is ever re-entered.
Callers: base/misc/ioctl.c base/keyboard/serv_8042.c base/keyboard/keyboard-server.c base/serial/ser_irq.c dosext/sound/sound.c dosext/net/net/pktnew.c
pic_resched decrements a count of interrupts on the stack (set by do_irq()). If the count is then less or equal to some pre-defined value (normally 1, pic_icount_od), pic_resched moves all queued interrupts to the interrupt request register.
Normally it is called from pic_iret(), but it can also be called directly if dosemu was fooled by the program and failed to catch iret.
pic_request triggers an interrupt. There is presently no way to "un-trigger" an interrupt. The interrupt will be initiated the next time pic_run is called, unless masked or superceded by a higher priority interrupt. pic_request takes one argument, an interrupt level, which specifies the interrupt to be triggered. If that interrupt is already active, the request will be queued until all active interrupts have been completed. The queue is only one request deep for each interrupt, so it is the responsibility of the interrupt code to retrigger itself if more interrupts are needed.
pic_iret is used to sense that all active interrupts are really complete, so that interrupts queued by pic_request can be triggered. Interrupts end when they issue an outb 0x20 to the pic, however it is not yet safe at that time to retrigger interrupts, since the stack has not been restored to its initial state by an iret. pic_iret is called whenever interrupts have been enabled by a popf, sti, or iret. It determines if an iret was the cause by comparing stack contents with cs and ip. If so, it calls pic_resched() and does the actual iret by pop'ing ip and cs from stack. It is possible for pic_iret to be fooled by dos code; for this reason active interrupts are checked, any queued interrupts that are also active will remain queued. Also, some programs fake an iret, so that it is possible for pic_iret to fail. See pic_watch for the watchdog timer that catches and fixes this event.
pic_watch is a watchdog timer for pending interrupts. If pic_iret somehow fails to activate a pending interrupt request for 2 consecutive timer ticks, pic_watch will activate them anyway. pic_watch is called ONLY by timer_tick, the interval timer signal handler, so the two functions will probably be merged.
This function returns a non-zero value if the designated interrupt has been requested and is not masked. In these circumstances, it is important for a hardware emulation to return a status which does *not* reflect the event(s) which caused the request, until the interrupt actually gets processed. This, in turn, hides the interrupt latency of pic from the dos software.
The single parameter ilevel is the interrupt level (see pic.h) of the interrupt of interest.
If the requested interrupt level is currently active, the returned status will depend upon whether the interrupt code has re-requested itself. If no re-request has occurred, a value of false (zero) will be returned.
pic_activate requests any interrupts whose scheduled time has arrived. anything after pic_dos_time and before pic_sys_time is activated. pic_dos_time is advanced to the earliest time scheduled.
pic_sched schedules an interrupt for activation after a designated time interval. The time measurement is in unis of 1193047/second, the same rate as the pit counters. This is convenient for timer emulation, but can also be used for pacing other functions, such as serial emulation, incoming keystrokes, or video updates. Some sample intervals:
rate/sec: 5 7.5 11 13.45 15 30 60 interval: 238608 159072 108459 88702 79536 39768 19884
rate/sec: 120 180 200 240 360 480 720 interval: 9942 6628 5965 4971 3314 2485 1657
rate/sec: 960 1440 1920 2880 3840 5760 11520 interval: 1243 829 621 414 311 207 103
pic_sched expects two parameters: an interrupt level and an interval. To assure proper repeat scheduling, pic_sched should be called from within the interrupt handler for the same interrupt. The maximum interval is 15 minutes (0x3fffffff).
Pic maintains two stacks of the current interrupt level. an internal one is maintained by run_irqs, and is valid whenever the emulator code for an interrupt is active. These functions maintain an external stack, which is valid from the time the dos interrupt code is called until the code has issued all necessary EOIs. Because pic will not necessarily get control immediately after an EOI, another EOI (for another interrupt) could occur. This external stack is kept strictly synchronized with the actions of the dos code to avoid any problems. pic_push and pic_pop maintain the external stack.