CS444
UART Receiver interrupts.
Look at $pcex/typewr.c: important points
numbered here.
/* typewr.c: echo on COM2 using UART receiver
interrupts
*/
/* The needed interrupt envelope routines
irq3int, etc. are
in */
/* the library, and sti, cli,
pic_enable_irq, etc., are there too. */
#include <stdio.h>
#include <serial.h>
#include <cpu.h>
#include <pic.h>
extern const IntHandler irq3inthand; /* the
assembler envelope routine */
void irq3inthandc(void); /* the C core
int handler, called from envelope */
int
done;
/* global for communication with int hand */
/* Note in our enviroment, interrupts are
normally enabled in the CPU, so we
use cli() as necessary even at
the very start of the program */
void main()
{
if (sys_get_console_dev() != COM2)
printf("Warning:
console not COM2, so echos won't show on console\n");
/* init global explicitly, so program
works properly on reruns */
done = 0;
cli();
/* disable ints while setting them up */ <---(1)
printf("Calling set_intr_gate (irq
= 3, n = 0x23) to arm interrupt...\n");
set_intr_gate(COM2_IRQ+IRQ_TO_INT_N_SHIFT,
&irq3inthand);
<---(2)
printf("Commanding PIC to
interrupt CPU for irq 3 (COM2_IRQ)\n");
pic_enable_irq(COM2_IRQ);
<---(3)
printf("enabling receiver
interrupts in UART...\n");
outpt(COM2_BASE+UART_IER,
UART_IER_RDI); /* RDI = receiver data int */ <---(4)
/* now hardware reg's are all set
up, time to allow CPU ints-- */
sti();
<---(5)
printf("...done, entering
almost-infinite loop\n");
while
(!done)
/* loop almost
forever...*/
<---(6)
;
/* here when user types 'q': shutdown
ints we started up */
cli();
<---(10)
pic_disable_irq(COM2_IRQ); /* done with irq 3 in PIC */
<---(11)
outpt(COM2_BASE+UART_IER, 0); /* stop
UART ints */
<---(12)
sti();
/* but other ints still there */
<---(13)
return
0;
}
/* this is called from irq3inthand, the
assembler interrupt envelope routine */
/* that saves the C scratch regs on the stack,
restores them after this, */
/* then does the iret
instruction.
*/
void irq3inthandc(void)
{
char ch;
pic_end_int();
/* notify PIC that its
part is done */ <---(7)
ch = inpt(COM2_BASE+UART_RX); /* read
char (ACKs UART too) */
<---(8)
if (ch == 'q')
done =
1;
/* set global flag to let main know */
outpt(COM2_BASE+UART_TX,ch); /* echo
char (Transmitter should be ready) */
}
<---(9)
The Assembly Language
Interrupt Handler Envelope
In the following code, the bold lines are the essential ones you should understand. The others date from
(old version) Linux and are only needed to guard against bad user-level code.
Source is$pclibsrc/irq3.s
#
like linux SAVE_MOST and RESTORE_MOST macros in irq.h
.text
.globl
_irq3inthand
KERNEL_DS
= 0x18
_irq3inthand:
cld # D bit gets
restored by iret (this can be dropped)
push %es # in case user code changes data segs (this can be
dropped)
push %ds # (this can be dropped)
pushl %eax # save C scratch
regs <---(1) register saves
pushl %edx
pushl %ecx
movl $KERNEL_DS, %edx # (this can be dropped)
mov %dx, %ds # (this can be dropped)
mov %dx, %es # (this can be dropped)
call _irq3inthandc # call C interrupt handler <---(2) call into
C
popl
%ecx
<---(3) register restores
popl %edx
popl %eax
pop %ds #(this can be dropped)
pop %es #(this can be dropped)
iret
<---(4) iret instruction