/* timetest1.c: show we can get old PC off stack in interrupt handler */
/* Note: stop its execution with reboot by ~r */
#include <stdio.h>
#include <timer.h>
#include <pic.h>
#include <cpu.h>
#define LINELEN 100
#define MILLION 1000000
extern const IntHandler irq0inthand; /* assembler envelope */
void set_timer_count(int count); /* local function, could be static */
int icount; /* global for int handler use */
void main()
{
int count;
char buf[LINELEN];
int i;
count = 30000; /* specify a number here to avoid input */
/* Note that too-small startcounts (under 100?) will cause problems */
/* because the PIC is bombarded with too-frequent interrupts */
/* printf("Enter startcount for timer (for example, 30000): "); */
/* fgets(buf,LINELEN,CONSOLE); *//* fgets takes dev# in SA lib vs. fd on UNIX */
/* sscanf(buf, "%d" ,&count); */
cli(); /* disable ints while setting them up */
printf("Setting IDT interrupt gate descriptor for irq 0 (int n = 0x20)\n");
set_intr_gate(TIMER0_IRQ+IRQ_TO_INT_N_SHIFT, &irq0inthand);
printf("Commanding PIC to interrupt CPU for irq 0 (TIMER0_IRQ)\n");
pic_enable_irq(TIMER0_IRQ);
printf("Commanding timer to generate pulse train using this count\n");
set_timer_count(count);
printf("Enabling interrupts in CPU\n");
icount = 0;
sti();
for (i=0;i<MILLION;i++) /* million loops of 100 passes on 400Mhz cpu: secs*/
work0(); /* (slowed down by printf in int handler) */
printf("Shutting down\n");
cli();
printf("saw %d interrupts\n",icount);
pic_disable_irq(TIMER0_IRQ);
sti();
}
int work0() {
int i, sum=0;
for (i=0; i<100; i++)
sum += i;
return 0;
}
/* Set up timer to count down from given count, then pulse, repeatedly */
/* These output pulses follow wires to the PIC to provide timer interrupts */
/* count of 0 sets max count, 65536 = 2**16 */
void set_timer_count(int count)
{
outpt(TIMER_CNTRL_PORT, TIMER0|TIMER_SET_ALL|TIMER_MODE_RATEGEN);
outpt(TIMER0_COUNT_PORT,count&0xff); /* set LSB here */
outpt(TIMER0_COUNT_PORT,count>>8); /* and MSB here */
}
/* for cs644, show that struct StackLayout works */
#define NREGS 5
typedef struct {
unsigned int regs[NREGS]; /* 5 regs pushed by irq0inthand */
unsigned int pc; /* the old PC we want */
unsigned short code; /* the interrupt code */
unsigned short cs; /* the old CS (code seg reg) */
unsigned int eflags; /* the old EFLAGS we want */
} StackLayout;
void irq0inthandc(StackLayout sm)
{
pic_end_int(); /* notify PIC with EOI command that its part is done */
/* old PC should be in idle loop, between main and set_timer_count */
kprintf("old PC = %x (main = %x)\n", sm.pc, (int)main);
/* show IF before interrupt and now-- */
kprintf("old IF = %d\n", (sm.eflags >> 9)&1); /* will be 1 */
kprintf("current IF = %d\n", (get_eflags() >> 9)&1); /* will be 0 */
icount++;
}
====================================================================================
irq0.s (in $pclibsrc) The assembly-language envelope around the C interrupt handler.
The lines marked "**" are essential. The others could be dropped in our simple
environment, where the segment registers never change.
# like linux SAVE_MOST and RESTORE_MOST macros in irq.h
.text
.globl _irq0inthand
KERNEL_DS = 0x18
_irq0inthand: cld # D bit gets restored to old val by iret
push %es # in case user code changes data segments
push %ds
pushl %eax # save C scratch regs **
pushl %edx # **
pushl %ecx # **
movl $KERNEL_DS, %edx
mov %dx, %ds # make sure our data segs are in use now
mov %dx, %es
call _irq0inthandc # call C interrupt handler **
popl %ecx # **
popl %edx # **
popl %eax # **
pop %ds
pop %es
iret # **