On handling interrupts An introduction to the basic issues affecting the design of code that performs servicing of interrupts Compare-and-branch • We make frequent use of program-loops, such as this one: xor %bx, %bx # initialize array-index again: … # <body of the loop goes here> … inc %bx # increment array-index cmp $16, %bx # index still below 16? jb again # yes, go through again … # otherwise ‘fall through’ The ‘cmp’ instruction • The instruction ‘cmp’ causes the CPU to perform internally a subtraction-operation • Example: cmp $16, %bx • The value 16 is subtacted from the value in register BX (but without changing BX) • However, the values of bits in the FLAGS register DO get changed, so as to reflect the result of that subtraction-operation! The FLAGS register Status-flags O F D F I F T F S F Z F 0 A F 0 P F 1 C F Control-flags Legend: ZF = Zero Flag SF = Sign Flag CF = Carry Flag PF = Parity Flag TF = Trap Flag OF = Overflow Flag IF = Interrupt Flag AF = Auxiliary FlagDF = Direction Flag Effect of ‘cmp $16, %bx’ • When this compare-instruction is executed the first time (while BX = 0x0000), the effect upon the FLAGS value is based on this subtraction: (binary zero) 0000000000000000 (binary 16) - 0000000000100000 ----------------------------------= 1111111111100000 • Thus SF=1, ZF=0, CF=1 (due to ‘borrow’), etc. The ‘jb again’ instruction • This conditional-jump instruction examines the setting of the CF-bit (the Carry-Flag) – If CF=1 (i.e., true), then control is transferred to backward, to the instruction at ‘again:’ – If CF=0 (i.e., false), then control continues forward normally (i.e., it “falls through” to the instruction that follows the conditional-jump • The CPU performs its ‘jump’ by adding a signed-integer to the value in register IP Recall the ‘Fetch-Execute’ cycle Fetch next instruction Advance instruction-pointer Decode fetched instruction Execute decoded instruction no INTR ? yes Interrupt Service Routine Recall the PC components Central Processing Unit Main Memory system bus I/O device I/O device I/O device I/O device These peripheral I/O components act autonomously (i.e.. independently of whatever the CPU might happen to be doing at any given moment) Asynchronous interruptions! • So programs can be ‘interrupted’ anytime (i.e., at the end of any fetch-execute cycle) • Maybe even, for example, right here: cmp $16, %bx jb again An interrupt could occur AFTER the cmp-operation has set the FLAGS, but BEFORE the jb-instruction has tested any of those FLAG-bits Consider the ‘yes’ case Fetch next instruction Advance instruction-pointer Decode fetched instruction Execute decoded instruction no INTR ? yes Interrupt Service Routine The Interrupt Service Routine may do some arithmetical or logical operations that cause FLAGS to be modified – and so invalidate the program’s conditional-jump! The CPU solves this problem • When an interrupt-request is recognized by the CPU, it automatically ‘saves’ the crucial elements of its program-context • Thereby it can ‘resume’ its main program (after it services the interrupt) with these crucial register-values restored: – The address of the next instruction (CS:IP) – The previous settings of the FLAGS bits The ‘interruption’ senario Programmable Interrupt Controller (PIC) INTR INTA Central Processing Unit (CPU) SS SP FLAGS CS Dynamic Random Assess Memory (DRAM) interrupt handler IP stack-area main program peripheral device (keyboard, mouse, timer, etc) Interrupt Vector Table The sequence of actions 1. 2. 3. 4. 5. 6. Some device issues an interrupt-request Interrupt Controller forwards it to CPU CPU saves FLAGS, CS, and IP on stack CPU issues acknowledgement to PIC PIC sends Interrupt ID-number to CPU CPU uses ID-number as index into IVT and loads CS and IP with vector-values What the ISR works with • The Interrupt Service Routine begins with the its stack setup like this: +4 Saved FLAGS value +2 Saved CS-value +0 Saved IP-value room here for the stack to grow downward as needed SS:SP (“top” of the stack) If the ISR needs to use any of the other CPU registers, it should push their values onto the stack before it modifies them, then pop those saved values back before returning What does ‘iret’ do? • An Interrupt Service Routine concludes by executing an ‘iret’ instruction -- to resume whatever program had gotten interrupted, and with its FLAGS restored as they were • So what ‘iret’ does is to ‘pop’ the top three word-values off the stack and into the IP, CS, and FLAGS registers, respectively A role played by the PIC • Before resuming an interrupted program, an interrupt-handler needs to let the PIC know that it has finished servicing the preceding interrupt-request, and that it now is safe to send another request • Otherwise the stack-area might grow too large if more and more new interrupts got sent while old ones were being serviced The EOI command • Here’s how an interrupt-handler sends an ‘End-Of-Interrupt’ notification to the PIC: mov $0x20, %al out %al, $0x20 # put EOI command-code in AL # output AL to port number 0x20 • NOTE: It’s pure coincidence that the same number (i.e., ‘$0x20’) is used twice in this code-fragment (but easy to remember it!) Our demo-program • To illustrate the PC’s interrupt-handling mechanism, we wrote a short program (named ‘tickdemo.s’) that you can study • It has two ‘threads-of-execution’, plus a ‘shared’ variable that both threads use (i.e., one as ‘writer’, the other as ‘reader’) • The main thread stores a new value into the IVT that points to the second thread A timer-tick will interrupt ‘main’ Our program structure ticks: 0 Our ‘isr’ thread Interrupt vector table = IVT[ 0x08 ] Our ‘main’ thread Vector number 8 will point to our ISR’s entry-point (instead of to a ‘default’ ISR within the ROM-BIOS) A “critical section” of code • Modifying the timer-tick interrupt-vector is uses a two-step instruction-sequence: – First change the vector’s lo-word (offset) – Then change the vector’s hi-word (segment) • What if an interrupt should occur between those two steps? • We MUST prevent that!! (why?) • So we’ll use the ‘cli’ and ‘sti’ instructions A thought-experiment • What do you think will happen if we try to reboot the machine (by using ‘int 0x19’) without first restoring Interrupt Vector 8? • You can just comment out the instruction mov %eax, %fs:0x0020 if you want to try out this experiment yourself In-class exercise • The BIOS startup-code programs the timer so that it issues interrupts at a steady rate of about 18.2 timer-ticks per second • Can you modify our interrupt-handler code so that it only increment the ‘ticks’ counter once-per-second? i.e., like a digital watch • HINT: You may need to add at least one new variable in this program’s data-area