CS 3803 Lab 6 Interrupts Assigned 2/25/10 Point Value: 30

Similar documents
The K Project. Interrupt and Exception Handling. LSE Team. May 14, 2018 EPITA. The K Project. LSE Team. Introduction. Interrupt Descriptor Table

+ Overview. Projects: Developing an OS Kernel for x86. ! Handling Intel Processor Exceptions: the Interrupt Descriptor Table (IDT)

The Purpose of Interrupt

UMBC. contain new IP while 4th and 5th bytes contain CS. CALL BX and CALL [BX] versions also exist. contain displacement added to IP.

Mechanisms for entering the system

Homework / Exam. Return and Review Exam #1 Reading. Machine Projects. Labs. S&S Extracts , PIC Data Sheet. Start on mp3 (Due Class 19)

Homework. Reading. Machine Projects. Labs. Intel 8254 Programmable Interval Timer (PIT) Data Sheet. Continue on MP3

Microkernel Construction

Chapter 12: INTERRUPTS

Microkernel Construction

An Interrupt is either a Hardware generated CALL (externally derived from a hardware signal)

Computer Labs: I/O and Interrupts

7/19/2013. Introduction. Chapter Objectives Upon completion of this chapter, you will be able to: Chapter Objectives 12 1 BASIC INTERRUPT PROCESSING

An Interrupt is either a Hardware generated CALL (externally derived from a hardware signal)

Chapter 12: Interrupts

Week 11 Programmable Interrupt Controller

CMSC 313 COMPUTER ORGANIZATION & ASSEMBLY LANGUAGE PROGRAMMING LECTURE 09, SPRING 2013

8086 Interrupts and Interrupt Responses:

Interrupts. Chapter 20 S. Dandamudi. Outline. Exceptions

Understanding the Interrupt Control Unit of the 80C186EC/80C188EC Processor

BASIC INTERRUPT PROCESSING

Operating Systems Engineering Recitation #3 (part 2): Interrupt and Exception Handling on the x86. (heavily) based on MIT 6.

PC Interrupt Structure and 8259 DMA Controllers

CHAPTER 6 INTERRUPT AND EXCEPTION HANDLING

IA32 Intel 32-bit Architecture

W4118: interrupt and system call. Junfeng Yang

Intel x86 instruction set architecture

2.5 Address Space. The IBM 6x86 CPU can directly address 64 KBytes of I/O space and 4 GBytes of physical memory (Figure 2-24).

Computer Architecture and System Software Lecture 06: Assembly Language Programming

These three counters can be programmed for either binary or BCD count.

SRI VENKATESWARA COLLEGE OF ENGINEERING AND TECHNOLOGY DEPARTMENT OF ECE EC6504 MICROPROCESSOR AND MICROCONTROLLER (REGULATION 2013)

Programming the 8259 PIC: A Tech-Tip Example and Boilerplate

Part I. X86 architecture overview. Secure Operating System Design and Implementation x86 architecture. x86 processor modes. X86 architecture overview

CS 550 Operating Systems Spring Interrupt

Systems Programming and Computer Architecture ( ) Timothy Roscoe

Come and join us at WebLyceum

ECE 485/585 Microprocessor System Design

CS609 Final Term Solved MCQs with References Without Repetitions 14/02/2013

iapx Systems Electronic Computers M

3.1 DATA MOVEMENT INSTRUCTIONS 45

Objectives. Saving Interrupt Vectors. Writing a Custom Interrupt Handler. Examples of use of System functions for Input-Output and Interrupts

complex instruction set compute. reduced instruction set compute FETCH&EXECUTE. Instruction Register -

Chapter 8 PC Peripheral Chips - Pt 3 Interrupts

EEM336 Microprocessors I. Data Movement Instructions

Buffer Overflow Attack

Interrupt handling and context switching

x86 architecture et similia

MPLAB X Debugging Techniques

Embedded Systems Programming

Basic Execution Environment

1. state the priority of interrupts of Draw and explain MSW format of List salient features of

ECE 391 Exam 1 Review Session - Spring Brought to you by HKN

For more notes of DAE

System Programming. Prof. Dr. Antônio Augusto Fröhlich. Nov 2006

MICROPROCESSOR ALL IN ONE. Prof. P. C. Patil UOP S.E.COMP (SEM-II)

COS 318: Operating Systems

UNIT-IV. The semiconductor memories are organized as two-dimensional arrays of memory locations.

PROTECTION CHAPTER 4 PROTECTION

CS 410/510. Mark P Jones Portland State University. Week 4: Memory Management

Pre-virtualization internals

Northern India Engineering College, Delhi (GGSIP University) PAPER I

Lab 2: 80x86 Interrupts

Chapters 2, 3: bits and pieces. Chapters 2 & 3. Chapters 2, 3: bits and pieces. Chapters 2, 3: bits and pieces. Using C. A last word about hardware

Department of Computer Science and Engineering

SOEN228, Winter Revision 1.2 Date: October 25,

x86 segmentation, page tables, and interrupts 3/17/08 Frans Kaashoek MIT

4) In response to the the 8259A sets the highest priority ISR, bit and reset the corresponding IRR bit. The 8259A also places

CSE 451 Autumn Final Solutions mean 77.53, median 79, stdev 12.03

Concurrent programming: Introduction I

Interrupt Handler: Top Half. Changwoo Min

CS 550 Operating Systems Spring System Call

CanSecWest Nicolás A. Economou Andrés Lopez Luksenberg

Program Control Instructions

sequence is not needed. (ROM space). Another application is to use the poll mode to expand the number of priority levels to more than 64.

M80C286 HIGH PERFORMANCE CHMOS MICROPROCESSOR WITH MEMORY MANAGEMENT AND PROTECTION

eaymanelshenawy.wordpress.com

Computer Architecture and Assembly Language. Practical Session 5

Computer Systems Lecture 9

Embedded Systems. Input/Output Programming

O S E. Operating-System Engineering. Thread Abstraction Layer Tal

COS 318: Operating Systems

6/29/2011. Introduction. Chapter Objectives Upon completion of this chapter, you will be able to:

Assembler Programming. Lecture 10

MICROPROCESSOR MICROPROCESSOR ARCHITECTURE. Prof. P. C. Patil UOP S.E.COMP (SEM-II)

Midterm Exam #2 Answer Key

Scott M. Lewandowski CS295-2: Advanced Topics in Debugging September 21, 1998

Chapter 3: Addressing Modes

Dr. Ramesh K. Karne Department of Computer and Information Sciences, Towson University, Towson, MD /12/2014 Slide 1

9/19/18. COS 318: Operating Systems. Overview. Important Times. Hardware of A Typical Computer. Today CPU. I/O bus. Network

MICROPROCESSORS & MICRO CONTROLLER COLLEGE OF ENGINEERING DEPARTMENT OF COMPUTER SCIENCE AND ENGINEERING QUESTION BANK

Chapter 3. Assembly Language Programming with 8086

AMD-K5. Software Development Guide PROCESSOR

Experiment 8 8 Subroutine Handling Instructions and Macros

PCI Bus & Interrupts

MOV Move INSTRUCTION SET REFERENCE, A-M. Description. Opcode Instruction 64-Bit Mode. Compat/ Leg Mode

Xosdev Chapter 1 [The Bootloader] by mr. xsism

iapx86 Protection Electronic Computers M

Description of the Simulator

MICROPROCESSOR TECHNOLOGY

Lecture-61 Initialization Control Word 2 (ICW2):

Transcription:

CS 3803 Lab 6 Interrupts Assigned 2/25/10 Point Value: 30 Purpose Interrupts are crucial to an operating system. They are used to get status and data from the timer, keyboard, and disk drives to mention a few. In this lab we will learn how to set up protected mode interrupts for the INTEL processor. A significant portion of this lab is based on the material in Fundamentals of Embedded Software by Daniel Lewis. Overview of interrupts The INTEL 8253 Programmable Interrupt Controller (PIC) is used in the PC to handle interrupts from external devices such as the timer and the keyboard. The PIC will handle interrupt requests (IRQs) from an external device, prioritize the request, and then inform the CPU of the interrupts. The PIC will send a signal to the CPU that an interrupt has occurred. The CPU will complete the execution of the current instruction and then send a signal back to the PIC informing the PIC that is ready to receive the interrupt. The PIC will then place the interrupt number on a data bus which the CPU will then read. The CPU will use the interrupt number as an index into the Interrupt Descriptor Table (IDT). The IDT is a structure used by the INTEL processor to handle interrupts in protected mode. The table contains up to 256 interrupt descriptors each of which point to the location of an Interrupt Service Routine (ISR). The CPU will index to a descriptor in the IDT and then transfer control to the ISR. When control is passed to an ISR, interrupts are disabled. If warranted, the ISR will reenable interrupts, process the interrupt, and then send an interrupt acknowledge signal to the PIC. The CPU will then return control to the program that was interrupted. When the PIC receives the acknowledgement, it will send other interrupts to the CPU if any are present. There are three classes of interrupts: Processor exceptions Hardware interrupts Software interrupts Processor interrupts include such actions as divided by zero and addressing outside of a segment limit. There will be about 20 processor interrupts that we will address. 1

A software interrupt is invoked by the INT instruction. We will not address these types of interrupts in this lab. Hardware interrupts are those that are requested from an external device such as the timer or keyboard. In this lab we will learn how to handle keyboard interrupts. Setting up the IDT The IDT is a table with a maximum of 256 descriptors. The descriptors contain the information needed to transfer control to an ISR. A descriptor is an 8 byte structure. There are three types of descriptors: Task Gate descriptor Trap Gate descriptor Interrupt Gate descriptor In this lab we will work with Interrupt Gate descriptors. We will use the IDT_DESCRIPTOR C declaration: typedef struct IDT_DESCRIPTOR WORD16 offset_0 ; /* Offset, bits 15-0 */ WORD16 selector ; /* Selector value */ BYTE8 not_used ; /* Must be set to 0's */ BYTE8 flags ; /* Access rights, etc. */ WORD16 offset_16; /* Offset, bits 31-16 */ attribute ((packed)) IDT_DESCRIPTOR ; The fields, offset_0 and offset_16, contain the offset of the ISR pointed to by the selector field. The selector is an entry in the Global Descriptor Table (GDT). The GDT was setup by GRUB to contain a segment of memory for our kernel. The IDT's selector points to this area of memory. The selector contains information such as the base address, length, and protection for this segment of memory. In a later lab we will examine the GDT in more depth. The flags field of the descriptor is concerned with access rights for the ISR. The IDT is an array declared as: IDT_DESCRIPTOR idt[256]; We need to initialize the IDT before we can use it. The Init_IDT function sets up the IDT. The if statement is used to determine if the interrupt handles processor exceptions (INTEL_INT) or hardware exceptions (PIC1_INT and PIC2_INT). We are interested in 2

the PIC interrupts. In this function, the hardware exceptions are set to ISRs that do nothing. Later we will reset them to meet our needs. A more complete set of code to initialize the IDT is found in Appendix A. void Init_IDT(void) extern void ISR_PIC1(void) asm("isr_pic1") ; extern void ISR_PIC2(void) asm("isr_pic2") ; IDT_DESCRIPTOR *p ; int i ; p = idt ; for (i = 0; i < ENTRIES(idt); i++, p++) ISR isr ; p->selector = code_selector ; p->not_used = 0x00 ; p->flags = 0x8E ; if (INTEL_INT(i)) isr = _intel_isr[i] ; else if (PIC1_INT(i)) isr = ISR_PIC1 ; else if (PIC2_INT(i)) isr = ISR_PIC2 ; else isr = ISR_User ; p->offset_0 = ((WORD16 *) &isr)[0] ; p->offset_16 = ((WORD16 *) &isr)[1] ; asm volatile ("lidt %0" : : "m" (idt_info)) ; To initialize the IDT to handle a different ISR the SetISR function is used. The function is passed an index into the IDT table and the address of the ISR. The address of the ISR is assigned to the offsets fields. typedef void (*ISR)(void) ; /* Pointer to an ISR */ void SetISR(int int_numb, ISR isr) IDT_DESCRIPTOR *p = &idt[int_numb] ; p->offset_0 = ((WORD16 *) &isr)[0] ; p->offset_16 = ((WORD16 *) &isr)[1] ; Other code of interest is shown below: 3

GLOBAL ISR_PIC1 ISR_PIC1: PUSH EAX MOV AL,20h OUT 020h,AL POP EAX IRET GLOBAL ISR_PIC2 ISR_PIC2: PUSH EAX MOV AL,20h OUT 020h,AL OUT 0A0h,AL POP EAX IRET SECTION.data ;---------------------------------------------------------------------------- ; Global Descriptor Table (GDT) ;---------------------------------------------------------------------------- gdt_info: DW (3*8)-1; 16-bit GDT limit DD gdt ; 32-bit GDT address gdt: null_descriptor: DW 0 ; seg length <0..15> DW 0 ; base address <0..15> DB 0 ; base address <16..23> DB 0 ; seg type and misc flags DB 0 ; seg length <16..19> & access flags DB 0 ; base address <24..31> code_descriptor: DW 0FFFFh ; seg length <0..15> DW 0 ; base address <0..15> DB 0 ; base address <16..23> DB 09Ah ; seg type and misc flags DB 0CFh ; seg length <16..19> & access flags DB 0 ; base address <24..31> data_descriptor: 4

DW 0FFFFh ; seg length <0..15> DW 0 ; base address <0..15> DB 0 ; base address <16..23> DB 092h ; seg type and misc flags DB 0CFh ; seg length <16..19> & access flags DB 0 ; base address <24..31> CODE_SELECTOR EQU code_descriptor - gdt DATA_SELECTOR EQU data_descriptor - gdt GLOBAL code_selector ; (Needed in init-idt.c) code_selector: DW CODE_SELECTOR Exercise 1 Create a new directory for Lab 5 and copy the files from your Lab 4 into this new directory. On the common drive is a set of starter files. Two of these files are init-idt.c and isr.asm. These files contain the code needed to initialize the IDT. Modify your files to use the lib.h file. This file contains common declarations that we will use for this and subsequent labs. lib.h is found in Appendix B. Add a dummy function to your kernel.c file for an enqueue function that we will use later in this lab. It signature is: BOOL Enqueue(int scancode) BOOL Enqueue(int scancode) return TRUE; asm("enqueue"); Re-assemble or compile these files. Link these files to your kernel. You will need to modify the link command to include the new.o files. At this point we have not called the Init_IDT function. Execute your kernel to make sure that it is still working properly. 5

Keyboard Interrupts Modern PCs have two PICs. The first is used to handle typical hardware interrupts. The second is used to handle additional interrupts and is cascaded off of the first PIC. To program the 8259 PICs, a series of up to 4 Initialization Command Words (ICW) are sent to each PIC. This is followed by up to 4 Operation Command Words (OCW) that are used to control the modes used by the PIC such as which interrupts are masked initially. Of interest to us in this lab are ICW2 and OCW1. ICW2 is used to inform the PIC what number should be passed to the CPU on the data bus when an interrupt is generated. When we initialized the IDT, specific entries were set aside for hardware interrupts. Specifically, entries 240 through 247 were set aside for PIC1 interrupts. PCs use IRQ0 for timer interrupts and IRQ1 for keyboard interrupts. How does the PIC know to send an interrupt number of 240 for a timer interrupt and a 241 for a keyboard interrupt? The answer is: ICW2 contains the base number for interrupts supported by the PIC. The IRQ number is added to the base number. The result is then sent to the CPU which uses it as in index into the IDT. Another function of the PIC is to mask off interrupts. OCW1 is used to control which IRQs are handled by the first PIC. The Init8259 function initializes the PICs and sets up the initial masks. These can be changed later as needed. The source code is found in Appendix C. Exercise 2 Add INIT8259.c to your Lab 4 directory. Call Init8259 from your kernel. Compile INIT8259.c and link it to your kernel. Execute your kernel to make sure that it is still working properly. Enabling Keyboard Interrupts To enable keyboard interrupts we need to create the keyboard ISR, modify the IDT to handle the ISR, and to unmask the PIC to enable the interrupts. Most ISRs are written in assembler unless the C complier supports the creation of interrupts. One approach is to write the interrupt portions of the interrupt in assembler and then call a C function to perform the intended functionality of the interrupt. This is the approach that we will take. 6

When an interrupt occurs, the CPU will disable interrupts. Depending on the nature of the interrupt, an ISR will: Save relevant registers Process the interrupt Re-enable interrupts (STI) Send an End-Of-Interrupt (EOI) command to the PIC And then return using the IRET instruction. STI is used at the beginning of an ISR if it wants to permit other interrupts to occur. IRET is needed instead of RET so that the CPU can correctly restore the program stack and return control to the interrupted program. A keyboard interrupt routine is provided. It saves the EAX register and then restores it before the routine returns. It checks to see if data is ready. If data is ready, it calls the C function, Enqueue, to save the character. In either case, it then sends the EOI command to the PIC. KeyboardISR: STI PUSHA IN AL,64h ; Keyboard Status Port AND AL,01h ; Data Ready? JZ KybdISR1 IN AL,60h ; Keyboard Data Port MOVZX EAX,AL PUSH EAX CALL Enqueue ADD ESP,4 KybdISR1: MOV AL,20h ;EOI command OUT 020h,AL POPA IRET In order to use this ISR, we will use the SetISR function to modify the IDT and then unmask IRQ1 so that keyboard interrupts are enabled. Add the InstallKeyboardISR function to your kernel and call the function after the Init8259 function call. Port 0x21 returns the current mask settings. Writing to port 0x21 will set the mask settings for the PIC. 7

extern void KeyboardISR(void) asm ("KeyboardISR") ; void InstallKeyboardISR(void) SetISR(IRQ2INT(IRQ_KYBD), KeyboardISR) ; outportb(0x21, inportb(0x21) & ~0x02) ; /* Unmask IRQ1 (Keyboard) */ Exercise 3 In your kernel call Init_IDT and then InstallKeyboardISR. Create a keyboard buffer with supporting Enqueue and Dequeue functions. Enqueue Add a character to the buffer Dequeue Removes character from the buffer The buffer should be a simple wrap-around array that is non-blocking. If the buffer is full, then the Enqueue function will return without adding a character. The character will be lost. If the buffer is empty, the Dequeue function should return 0. NOTE: It is important to disable interrupts around critical sections of code. The best way of doing this is to use: PUSHF ; CLI ; before the code and then use POPF ; STI; after the code. I recommend doing this for the enqueue and dequeue functions. I would also add it to the update_cursor function for the four outportb calls. The PUSHF, POPF, CLI, and STI statements are in-line assembly declarations for their equivalent assembly language instructions. The definitions of these functions are found in lib.h. To make the Enqueue function visible to the keyboard ISR, use the following statement before your definition of Enqueue: 8

BOOL Enqueue(BYTE8 scan_code) asm("enqueue") ; Modify your getchar function to use the dequeue function instead of polling the keyboard as you did in Lab3. Execute your kernel to make sure that it functions the same way that Lab 3 did except now your are using interrupts. 9

Appendix A /* ============================================================ */ /* File: INIT-IDT.C */ /* */ /* ============================================================ */ #include "lib.h" #define _HW_INT_BASE (256-16) /* Use last 16 vectors */ /* -------------------------------------------------------------- */ /* Format of an entry in the Interrupt Descriptor Table (IDT). */ /* Note: Not needed if you use SetISR (see below). */ /* -------------------------------------------------------------- */ typedef struct IDT_DESCRIPTOR WORD16 offset_0 ; /* Offset, bits 15-0 */ WORD16 selector ; /* Selector value */ BYTE8 not_used ; /* Must be set to 0's */ BYTE8 flags; /* Access rights, etc. */ WORD16 offset_16; /* Offset, bits 31-16 */ attribute ((packed)) IDT_DESCRIPTOR ; /* -------------------------------------------------------------- */ /* Operand used by the LIDT instruction. */ /* Note: Not needed if you use SetISR (see below). */ /* -------------------------------------------------------------- */ typedef struct IDT_INFO WORD16 limit ; DWORD32 addrs ; attribute ((packed)) IDT_INFO ; extern WORD16 code_selector asm ("code_selector") ; #define INTEL_INT(n) (0 <= n && n < 32) #define PIC1_INT(n) (IRQ2INT(0) <= n && n <= IRQ2INT(7)) #define PIC2_INT(n) (IRQ2INT(8) <= n && n <= IRQ2INT(15)) #define PRIVATE static PRIVATE void Abort(char *msg) CLI ; print(msg) ; print("; system halted.") ; for (;;) ; PRIVATE void ISR00(void) Abort("Divide error"); PRIVATE void ISR01(void) Abort("Debug Interrupt"); PRIVATE void ISR02(void) Abort("Non-Maskable Interrupt"); PRIVATE void ISR03(void) Abort("Breakpoint Interrupt"); PRIVATE void ISR04(void) Abort("Overflow"); PRIVATE void ISR05(void) Abort("BOUND Range Exceeded"); 10

PRIVATE void ISR06(void) Abort("Invalid Opcode"); PRIVATE void ISR07(void) Abort("No Math CoProcessor"); PRIVATE void ISR08(void) Abort("Double Fault"); PRIVATE void ISR09(void) Abort("Math CoProcessor Overrun"); PRIVATE void ISR10(void) Abort("Invalid TSS"); PRIVATE void ISR11(void) Abort("Segment Not Present"); PRIVATE void ISR12(void) Abort("Stack Segment Fault"); PRIVATE void ISR13(void) Abort("General Protection"); PRIVATE void ISR14(void) Abort("Page Fault"); PRIVATE void ISR16(void) Abort("Math CoProcessor Error"); PRIVATE void ISR17(void) Abort("Alignment Check"); PRIVATE void ISR18(void) Abort("Machine Check"); PRIVATE void ISR19(void) Abort("SIMD floating-point exception"); PRIVATE void ISR_Rsvd(void) Abort("Intel Reserved Interrupt"); PRIVATE void ISR_User(void) Abort("Unassigned Interrupt"); PRIVATE ISR _intel_isr[] = ISR00, /* Divide by zero */ ISR01, /* Debug exception */ ISR02, /* NMI */ ISR03, /* One byte interrupt */ ISR04, /* Interrupt on overflow */ ISR05, /* Array bounds error */ ISR06, /* Invalid opcode */ ISR07, /* Math not available */ ISR08, /* Double fault */ ISR09, /* Math segment overflow */ ISR10, /* Invalid TSS */ ISR11, /* Segment not present */ ISR12, /* Stack fault */ ISR13, /* General protection */ ISR14, /* Page fault */ ISR16, /* Math error */ ISR17, /* Alignment Check */ ISR18, /* Machine Check */ ISR19, /* SIMD floating-point except.*/ ISR_Rsvd /* Reserved */ ; PRIVATE IDT_DESCRIPTOR idt[256] ; PRIVATE IDT_INFO idt_info = sizeof(idt) - 1, (DWORD32) idt ; int IRQ2INT(int irq) 11

return _HW_INT_BASE + irq ; ISR GetISR(int int_numb) IDT_DESCRIPTOR *p = &idt[int_numb] ; DWORD32 offset ; offset = (((DWORD32) p->offset_16) << 16) + p->offset_0 ; return (ISR) offset ; void SetISR(int int_numb, ISR isr) IDT_DESCRIPTOR *p = &idt[int_numb] ; p->offset_0 = ((WORD16 *) &isr)[0] ; p->offset_16 = ((WORD16 *) &isr)[1] ; /* Hide this function's name from C programs! */ //void Init_IDT(void) asm ("Init_IDT") ; void Init_IDT(void) extern void ISR_PIC1(void) asm("isr_pic1") ; extern void ISR_PIC2(void) asm("isr_pic2") ; IDT_DESCRIPTOR *p ; int i ; p = idt ; for (i = 0; i < ENTRIES(idt); i++, p++) ISR isr ; p->selector = code_selector ; p->not_used = 0x00 ; p->flags = 0x8E ; if (INTEL_INT(i)) isr = _intel_isr[i] ; else if (PIC1_INT(i)) isr = ISR_PIC1 ; else if (PIC2_INT(i)) isr = ISR_PIC2 ; else isr = ISR_User ; p->offset_0 = ((WORD16 *) &isr)[0] ; p->offset_16 = ((WORD16 *) &isr)[1] ; asm volatile ("lidt %0" : : "m" (idt_info)) ; 12

Appendix B /* ============================================================ */ /* File: lib.h */ /* */ /* ============================================================ */ #ifndef LIB_H /* Avoid multiple inclusions */ #definelib_h /* A few data types to make the operand size more obvious. */ typedef int BOOL ; typedef unsigned char BYTE8 ; typedef unsigned short int WORD16 ; typedef unsigned long int DWORD32 ; typedef void (*ISR)(void) ; /* Pointer to an ISR */ #define BYTE unsigned char #define WORD unsigned short #define DWORD unsigned int /* Constants for use with data type BOOL (above). */ #ifndef TRUE #definetrue 1 #endif #ifndef FALSE #definefalse 0 #endif /* Macros to extract the LSByte and MSByte of a WORD16 value */ #definelsb(u) ((u) & 0xFF) #definemsb(u) ((u) >> 8) /* Returns number of elements in an array. (Use in for loops.) */ #defineentries(a) (sizeof(a)/sizeof(a[0])) 13

/* Declaration prefix to hide an object from the linker. */ #defineprivate static /* Define a NULL pointer. */ #ifndef NULL #definenull ((void *) 0) #endif /* 386 instructions needed when writing ISR's. Note that IRET */ /* pops the pointer to the stack frame that was established by */ /* code that the compiler generates at every function entry. */ #definepushcs asm volatile ("PUSHL %CS") ; #definepushf asm volatile ("PUSHFL") #definepopf asm volatile ("POPFL") #definesti asm volatile ("STI") #definecli asm volatile ("CLI") #definepusha asm volatile ("PUSHAL") #definepopa asm volatile ("POPAL") #defineenter asm volatile ("ENTER $0,$0") #defineleave asm volatile ("LEAVE") #defineiret asm volatile ("IRET") /* Support for functions implemented in IO.ASM */ void outportb(word16, BYTE8) ; BYTE8 inportb(word16) ; int IRQ2INT(int irq) ; ISR GetISR(int int_numb) ; void SetISR(int int_numb, ISR isr) ; /* Support for functions implemented in KEYBOARD.C */ BYTE8 GetScanCode(void) ; BOOL ScanCodeRdy(void) ; BOOL SetsKybdState(BYTE8) ; WORD16 ScanCode2Ascii(BYTE8 code) ; #endif 14

Appendix C INIT8259.C /* -------------------------------------------------------------- */ /* File: INIT8259.C */ /* */ /* -------------------------------------------------------------- */ #include "lib.h" #define PIC1_BASE_VCTR IRQ2INT(0) #define PIC2_BASE_VCTR IRQ2INT(8) /* 8259 Command and Data ports... */ #define PIC1_CMND 0x20 #define PIC1_DATA 0x21 #define PIC2_CMND 0xA0 #define PIC2_DATA 0xA1 /* Bit fields in 1st Control Word (ICW1) */ #define ICW1_USE_ICW4 0x01 /* 0/1 => ICW4 no/yes */ #define ICW1_SINGLE 0x02 /* 0/1 => cascaded/single PICs */ #define ICW1_INTERVAL4 0x04 /* 0/1 => MOD 8/4 vectors */ #define ICW1_LEVEL 0x08 /* 0/1 => Edge/Level triggered */ #define ICW1_INIT 0x10 /* 1 => Initialization */ #define ICW1_BASE_VCTR 0xE0 /* Bit field (not value) */ /* Bit fields in 2nd Control Word (ICW2) */ #define ICW2_BASE_VCTR 0xFF /* Bit field (not value) */ /* Bit fields in 3rd Control Word (ICW3) */ #define ICW3_SLAVE_ID 0x07 /* Bit field (not value) */ #define ICW3_IR7_SLAVE 0x80 #define ICW3_IR6_SLAVE 0x40 #define ICW3_IR5_SLAVE 0x20 #define ICW3_IR4_SLAVE 0x10 #define ICW3_IR3_SLAVE 0x08 #define ICW3_IR2_SLAVE 0x04 #define ICW3_IR1_SLAVE 0x02 #define ICW3_IR0_SLAVE 0x01 /* Bit fields in 4th Control Word (ICW4) */ #define ICW4_8086 0x01 /* 8086/88 (MCS-80/85) mode */ #define ICW4_AUTO 0x02 /* Auto (normal) EOI */ 15

#define ICW4_BUF_SLAVE 0x08 /* Buffered mode/slave */ #define ICW4_BUF_MASTER 0x0C /* Buffered mode/master */ #define ICW4_SFNM 0x10 /* Special fully nested (not) */ #define PIC1_ICW1 (ICW1_INIT ICW1_USE_ICW4) #define PIC1_ICW2 PIC1_BASE_VCTR #define PIC1_ICW3 ICW3_IR2_SLAVE #define PIC1_ICW4 ICW4_8086 #define PIC2_ICW1 (ICW1_INIT ICW1_USE_ICW4) #define PIC2_ICW2 PIC2_BASE_VCTR #define PIC2_ICW3 2 /* PIC2 Slave ID # */ #define PIC2_ICW4 ICW4_8086 #define PIC1_OCW1 0xFB /* Mask interrupts except PIC2 */ #define PIC2_OCW1 0xFF /* Mask off all interrupts */ /* Hide this function's name from C programs! */ //void Init8259(void) asm ("Init8259") ; void Init8259(void) PUSHF ; CLI ; outportb(pic1_cmnd, PIC1_ICW1) ; outportb(pic2_cmnd, PIC2_ICW1) ; /* Position hardware vectors just after Intel vectors */ outportb(pic1_data, PIC1_ICW2) ; outportb(pic2_data, PIC2_ICW2) ; /* Master: 20h/21h (ch 2), Slave: A0h/A1h (ID #2) */ outportb(pic1_data, PIC1_ICW3) ; outportb(pic2_data, PIC2_ICW3) ; /* 8086 mode (use vectors) */ outportb(pic1_data, PIC1_ICW4) ; outportb(pic2_data, PIC2_ICW4) ; /* mask all interrupts */ outportb(pic1_data, PIC1_OCW1) ; outportb(pic2_data, PIC2_OCW1) ; POPF ; 16