Assembly
Language
© Copyright Brian Brown, 1988-2000. All rights reserved.
| Notes | Home
Page |
Describe the problems associated with the timing of
interrupts with respect to.
The use of interrupts can affect the timing of existing coded routines. This effect is unpredictable, due to the nature of interrupts. Code which is heavily timing dependent may not work when interrupts are enabled. These timing critical portions need to be protected also.
The processing of interrupts should be kept as short as possible, with requard to the duration of the software event in question.
The interrupts must be arranged in priority. This can be done using either software (polling order) or hardware (use a PIC). It might be that higher priority interrupts keep occurring, preventing a lower level interrupt from being serviced.
Decisions need to be made whether interrupt routines can also be nested. This could mean servicing a high priority interrupt which allows a lower priority interrupt inside it.
The use of a daisy chain or PIC system ensures correct timing with respect to other interrupt sources. It overcomes the problems of multiple interrupts occurring and their associated priorities.
The programmer must decide, for each interrupt in the system, which must be
Some processors with limited stack space could experience problems with a large number of nested interrupts.
State the problems of working with multiple interrupts with
respect to.
The purpose of interrupt routines are to ensure adequate response times to requests for service. Problems may occur if a lower level interrupt routine will not allow a higher level interrupt to interrupt it. The designer works out worst case scenarios for all interrupts in the system, which will highlight any potential problems. Ensuring that higher priority interrupts can interrupt a lower level routine is important in situations like power-down.
To keep the interrupt routines short, a buffered or message approach might be adopted. This ensures the device is serviced quickly, but that interrupt routines do not consume excessive amounts of processor time.
Interrupt priority is determined by
This might mean that an interrupt driven printer spooler would have lower priority than an interrupt routine which services a floppy disk unit. Conversely, the highest priority in the system could be refreshing the Dynamic RAM.
The three main methods are,
Status ports are suitable for systems which are not very critical and the priority may need to be changed dynamically. Hardware vector systems are very fast at servicing interrupts, thus are used where response times are critical.
PIC systems combine the features of both the status and hardware vector systems, and are used in complicated systems with a large number of interrupt sources, some of which may need to be periodically disabled/enabled.
TIMING DEVICES
State the different timing devices available, and give examples where each is most appropriately used, eg,
Timing devices and methods are used in computer systems for
Software methods
These involve the calculation of the time period using software.
The advantage is no/little hardware and easy programming, at the
expense of accuracy.
Hardware methods
These involve the use of support chips like a Real Time Clock
(RTC), timer/counter chips and interrupts. They have greater
accuracy and resolution at the expense of interrupt routines and
extra hardware.
Software Loops
These are implemented by loading a register with a value, then
decrementing and looping till the register value reaches zero,
eg.
DELAY: Mov CX, FFFFh DELAY1: Dec CX Jne DELAY1 Ret
It is generally used to implement small delays, examples being
and where variances in iteration speed are not critical (accuracy is not important).
Longer delays are achieved by using another register to provide multiple calls, eg.
Mov BX, 5 Lp1: CALL DELAY Dec BX Jne Lp1
The processor is tied up for the duration of the software loop.
Modern processors using high clock rates can buzz through a 16bit timing loop very quickly.
8086 Code Example mov cx, 0 ;4 clocks delay: loop delay ;17 clocks ;5 non-branch 10mHz -> ; 100ns per clock cycle loop repeats ffffh * 17 clocks = 1114112 clocks = 111,411,200 nanoseconds = 111.4 milliseconds
though it will actually perform faster due to the instruction queue cache.
Advantages
Disadvantages
RTC in hardware
A peripheral chip, driven by the system clock, is used for timing
purposes. It has registers which are programmed with specific
time intervals. Each system clock decrements this value at a
pre-determined rate (divide by 1,2,4,8,16 etc). When the register
count reaches zero, the RTC chip generates an interrupt to the
processor.
Most chips provide several channels which can either be used separately, or cascaded together for longer time intervals.
Other chips available are DATE/TIME calendar chips, which are used to implement time of day features for the computer operating system.
Real-time hardware implementations are suitable for
Advantages
Disadvantages
RTC in software
In this system, a periodic interrupt is used to implement timing
features, either loop counts or time of day calendar functions.
The frequency of the interrupt determines the resolution and accuracy of the clock. On each interrupt the service routine might be,
; assume interrupt occurs at mains frequency, every 20ms
; could be mains derived
extern int ticks, seconds, minutes, hours;
void interrupt clock()
{
++ticks;
if( ticks >= 50 ) {
ticks = 0; seconds++;
if( seconds >= 60 ) {
seconds = 0; minutes++;
if( minutes >= 60 ) {
minutes = 0; hours++;
if( hours >= 24 ) hours = 0;
}
}
}
}
This implementation requires the variables to be held in RAM space, thus the clock only works when power is on and the system has been initialised.
Advantages
Disadvantages
4 RTE controlled delay
In a multi-user system, the operating system controls delays
which may be imposed upon tasks for any number of reasons.
Tasks may also voluntarily request suspension. Each suspended task is inserted into a sleep queue, which is driven by a real-time clock. The entry for each task corresponds to the number of timer ticks before the task is to be rewaken.
If the timer tick is every 100microseconds, and taskA requests a sleep period of 400milliseconds, then the queue looks like,
taskA = 4000
If another task, taskB then requests a sleep period of 800 milliseconds, the queue looks like,
taskA = 4000 taskB = 4000
Once the time has expired, the operating system removes the task from the sleep queue.
The following code example illustrates a simple implementation for tasks to sleep under the control of the RTE.
void far wakeup( int task_number ) /* unblock a task */
{
disable(); /* turn off interrupts */
PTN(process_state) = READY; /* change task state */
enable(); /* re-enable interrupts */
}
void far sleep(unsigned int clockticks) /* voluntary task sleep */
{
disable(); /* turn off interrupts */
PTN(process_state) = SLEEPING; /* change task state to SLEEPING */
PTN(sleep_rate) = clockticks; /* fill in duration to sleep */
enable(); /* re-enable interrupts */
asm hlt; /* interrupt call to transfer(); */
}
void far interrupt transfer( void )
{
PTN(SS) = (unsigned int) _SS; /* save current running task Stack
pointers*/
PTN(SP) = (unsigned int) _SP;
if( PTN(process_state) == RUNNING) /* if a running task, change to
ready*/
PTN(process_state) = READY;
task_number++; /* select next task to run */
if( task_number > (NUM_TASKS-1) ) task_number = 0;
/* skip tasks which are blocked and sleeping. for sleeping tasks
decrement their sleep_rate count, and if zero, alter their
task state to READY.
For blocked tasks, see if they have recieved a message, and if
they have, change their task state to READY
*/
while((PTN(process_state) == BLOCKED)
||(PTN(process_state) == SLEEPING)) {
if( PTN(process_state) == SLEEPING) {
PTN(sleep_rate--);
if( PTN(sleep_rate)==0) wakeup(task_number);
}
if((PTN(process_state) == BLOCKED)
&& (PTN(msg) == TRUE)) {
PTN(process_state) = READY;
PTN(msg) = FALSE;
}
task_number++;
if( task_number > (NUM_TASKS-1) ) task_number = 0;
}
/* swap to stack of newly selected task */
................;
................;
outportb( PIC, EOI ); /* signal EOI to 8239 PIC */
}
Software Polling using RS232 on IBM-PC
This technique involves using the CPU to detect when the device
is ready to present data to it. In our case, it involves
continually reading the RBF flag of the Line Status Register for
the serial port.
When RBF is asserted, the CPU can then read the character from the receive buffer register of the serial card. The following program shows a C program which performs software polling of the serial port to receive characters from a transmitter station.
/* ser1.c, Software poll of receive side of serial port */
#define TRUE 1
#define FALSE 0
#define RBR 0x3f8
#define THR 0x3f8
#define LSR 0x3fd
#define IER 0x3f9
#define BYTE unsigned char
#define DATAREADY 1
#define OVERFLOW 2
#define OVEREADY 3
#include <conio.h>
#include <stdio.h>
#include <dos.h>
static int EXIT = FALSE;
union REGS rgs;
static BYTE attribute = 0x0f;
void rs232_init(void) {
outportb( IER, 0x00 );
outportb( 0x3fb,0x80); /* access baud rate generator */
outportb( 0x3f8,0x0c); /* 9600 baud */
outportb( 0x3f9,0x00);
outportb( 0x3fb,0x03); /* 8 bits, no parity, one stop*/
}
void send( BYTE ch) {
while( (inportb(LSR) & 0x20) != 0x20) ;
outportb( THR, ch );
}
BYTE recieve( void ) {
return( inportb( RBR ) );
}
void scrollup( tlc, tlr, brc, brr )
unsigned int tlc, tlr, brc, brr;
{
rgs.h.al = 1; rgs.h.ah = 6;
rgs.h.ch = tlr; rgs.h.cl = tlc;
rgs.h.dh = brr; rgs.h.dl = brc;
rgs.h.bh = 7; int86( 0x10, &rgs, &rgs );
}
void writechar( BYTE ch, BYTE attribute ) {
rgs.h.ah = 15; int86( 0x10, &rgs, &rgs );
rgs.h.ah = 9; rgs.x.cx = 1;
rgs.h.al = ch; rgs.h.bl = attribute;
int86( 0x10, &rgs, &rgs );
}
main() {
BYTE recch, LSRSTATE;
unsigned int key;
static int recx = 35, recy = 3, transx = 2, transy = 3;
recch = inportb( RBR ); /* dummy read of RBR to clear */
clrscr();
printf("+---------------------+ +---------------------+\n");
printf("|Transmit Window: | |Recieve Window: |\n");
printf("| | | |\n");
printf("| | | |\n");
printf("| | | |\n");
printf("| | | |\n");
printf("| | | |\n");
printf("| | | |\n");
printf("| | | |\n");
printf("| | | |\n");
printf("| | | |\n");
printf("| | | |\n");
printf("| | | |\n");
printf("| | | |\n");
printf("| | | |\n");
printf("| | | |\n");
printf("+---------------------+ +---------------------+\n");
printf("\n Type keys to transmit to other PC.\n");
printf(" Incoming data displayed in recieve window.\n");
printf(" Press F10 to STOP.\n");
while( EXIT == FALSE ) {
if( kbhit() != 0) {
gotoxy( transx, transy );
key = getch();
if( key == 0x00 ) EXIT = TRUE; /* F10 */
else {
send( (BYTE) (key & 0xff) );
putchar( key & 0xff );
transx = transx + 1;
if( transx > 29 ) {
transx = 2;
transy++;
if( transy > 16 ) {
scrollup(1,2,28,15);
transy = 16;
}
}
}
}
else {
LSRSTATE = inportb( LSR ) & 3;
if( (LSRSTATE > 0) && (LSRSTATE < 4) ) {
switch( LSRSTATE ) {
case OVERFLOW :
case OVEREADY :
attribute |= 0x80;
putchar( 0x07 );
break;
case DATAREADY :
attribute = 0x0f;
break;
}
recch = recieve();
gotoxy( recx, recy );
writechar( recch, attribute );
recx = recx + 1;
if( recx > 71 ) {
recx = 35;
recy++;
if( recy > 16 ) {
scrollup(34,2,71,15);
recy = 16;
}
}
}
}
}
}
Interrupt Driven RS232 on IBM-PC
This technique involves using the I/O device to tell the CPU when
it is ready to present data. This frees the CPU for other tasks
during this time, and also guarantees adequate response times. In
our case, it involves enabling interrupt driven operation for the
receive section of the serial port.
When a character arrives, the serial port will issue an interrupt request to the computers Priority Interrupt Controller chip (PIC). The PIC will interrupt the CPU and place the vector of the receive routine onto the data bus. The CPU uses this vector as an entry into a lookup table which then generates the address of the routine servicing that interrupt.
The receive interrupt routine reads the character stored in the RBR and deposits it into a circular queue buffer. Once the interrupt is serviced, the PIC must be reset to enable further interrupts to take place.
The 8259 Priority Interrupt Controller
This chip is reponsible for prioritizing interrupts from various
I/O devices and passing them onto the CPU for servicing. When a
device requests service, it asserts its associated interrupt line
(IRQ0 to IRQ7), signalling the PIC. The PIC asserts IRQ to the
processor which responds with INTA (Interrupt Acknowledge). Upon
receiving this, the PIC presents a vector byte on the data bus
which the CPU uses to determine the address of the interrupt
service routine.
In the IBM-PC, the association of IRQ's to I/O devices is,
Interrupt Device Vector byte NMI Parity and 8087 device IRQ0 Timer/Clock output 08 IRQ1 Keyboard 09 IRQ2 EGA/PC network 0A IRQ3 COM2 0B IRQ4 COM1 0C IRQ5 Fixed disk controller XT 0D IRQ6 Floppy disk controller 0E IRQ7 Lpt1 0F PIC port addresses are 0x20 and 0x21
At port 0x21 is the PIC MASK REGISTER. Each bit in the mask register corresponds to an IRQ line. IRQ0 is associated with bit 0 and so on. Writing a ZERO to a selected bit enables the interrupt line for that particular bit.
When the PC is reset or powered on, all interrupt devices are disabled by writing 0x0f to the PIC mask register. The ROM BIOS/DOS routines enable IRQ0, IRQ1, IRQ6 and IRQ7 as part of the system initialisation.
The serial card on the PC uses IRQ4. Enabling bit 4 of the PIC mask register will thus also enable the reception of interrupts from the serial card.
To prevent destroying the current state of the mask register, it is first read then bit 4 is enabled. The new mask setting is then written back to the mask register.
Enabling of interrupt operation on the serial card
The Interrupt Enable register (IER) must be programmed for
receive interrupt generation. This is achieved by the statement
outportb( IER, 1 );
The PC serial card also has a little bit which needs to be set in order to allow interrupts to be passed onto the PIC. This bit must be set, and is done by the statement
outportb( MCR, 0x0b ); /* bit 0 = DTR, bit 1 = RTS, bit 3 = out2 */
Setting the interrupt vector to the receive interrupt
routine
This must be done before enabling the PIC controller, otherwise
it might cause the system to accept interrupts and branch to a
non-existent service routine.
The code which installs the interrupt service routine is,
setvect( 0x0c, recieve );
This places the segment:offset address of the service routine into the interrupt vector for interrupt 0x0c. This is the vector which the PIC chip presents when a device asserts IRQ4.
Enabling of the PIC to recognize the serial card
The PIC controller can provide masking of the various IRQ lines
coming from I/O cards. The PC serial card normally uses IRQ4 for
COM1. This interrupt must be programmed for acceptance by the
PIC.
The statements that achieve this are,
mask = inportb( PIC_MASK ); mask = mask & 0xef; /* enable IRQ4 */ outportb( PIC_MASK, mask );
Also, after servicing the interrupt, the PIC must be reset to allow further interrupts, by the command,
outportb( PIC, 0x20 );
The following program shows a C program which performs interrupt driven operation of the serial port to receive characters from a TRANSMITTER station.
/* ser2.c, Recieve interrupt driven serial port */
#define TRUE 1
#define FALSE 0
#define RBR 0x3f8
#define THR 0x3f8
#define LSR 0x3fd
#define IER 0x3f9
#define MCR 0x3fc
#define BYTE unsigned char
#define PIC 0x20
#define PIC_MSK 0x21
#define maxsize 4096
#include <conio.h>
#include <stdio.h>
#include <dos.h>
static int EXIT = FALSE;
union REGS rgs;
static BYTE attribute = 0x07;
static BYTE mask;
static void interrupt (*oldint)();
static BYTE buffer[maxsize];
static BYTE *inptr, *outptr;
static int char_ready = FALSE;
static int overrun = FALSE;
void interrupt recieve( void );
void rs232_install(void) {
outportb( 0x3fb,0x80); /* access baud rate generator */
outportb( 0x3f8,0x0c); /* 9600 baud */
outportb( 0x3f9,0x00);
outportb( 0x3fb,0x03); /* 8 bits, no parity, one stop*/
oldint = getvect( 0x0c );
setvect( 0x0c, recieve ); /* install com1 routine */
outportb( MCR, 0x0b ); /* enable DTR and OUT2 */
outportb( IER, 1 );
mask = inportb( PIC_MSK ); /* enable com1 on pic */
mask = mask & 0xef;
outportb( PIC_MSK, mask );
}
void rs232_deinstall( void ) {
mask = inportb( PIC_MSK );
mask = mask | 0x10;
outportb( PIC_MSK, mask );
outportb( IER, 0 );
outportb( MCR, 0 );
setvect( 0x0c, oldint );
}
void send( BYTE ch) {
while( (inportb(LSR) & 0x20) != 0x20) ;
outportb( THR, ch );
}
void interrupt recieve( void ) {
disable();
*inptr = inportb( RBR );
++inptr;
if( inptr > buffer + maxsize ) inptr = buffer;
char_ready = TRUE;
if( (inportb( LSR ) & 0x02) == 0x02) overrun = TRUE;
else overrun = FALSE;
enable();
outportb( PIC, 0x20); /* signal EOI to 8239 PIC chip */
/* there is an IRET here */
}
void scrollup( tlc, tlr, brc, brr )
unsigned int tlc, tlr, brc, brr;
{
rgs.h.al = 1; rgs.h.ah = 6;
rgs.h.ch = tlr; rgs.h.cl = tlc;
rgs.h.dh = brr; rgs.h.dl = brc;
rgs.h.bh = 7; int86( 0x10, &rgs, &rgs );
}
void writechar( BYTE ch, BYTE attribute ) {
rgs.h.ah = 9; rgs.h.bh = 0;
rgs.x.cx = 1; rgs.h.al = ch;
rgs.h.bl = attribute; int86( 0x10, &rgs, &rgs );
}
main() {
BYTE recch;
unsigned int key;
static int recx = 35, recy = 3, transx = 2, transy = 3;
inptr = buffer;
outptr = buffer;
rs232_install();
recch = inportb( RBR ); /* dummy read of RBR to clear */
clrscr();
printf("+---------------------+ +---------------------+\n");
printf("|Transmit Window: | |Recieve Window: |\n");
printf("| | | |\n");
printf("| | | |\n");
printf("| | | |\n");
printf("| | | |\n");
printf("| | | |\n");
printf("| | | |\n");
printf("| | | |\n");
printf("| | | |\n");
printf("| | | |\n");
printf("| | | |\n");
printf("| | | |\n");
printf("| | | |\n");
printf("| | | |\n");
printf("| | | |\n");
printf("+---------------------+ +---------------------+\n");
printf("\n Type keys to transmit to other PC.\n");
printf(" Incoming data displayed in recieve window.\n");
printf(" Press F10 to STOP.\n");
while( EXIT == FALSE ) {
if( kbhit() != 0) {
gotoxy( transx, transy );
key = getch();
if( key == 0x00 ) EXIT = TRUE;
else {
send( (BYTE) (key & 0xff) );
putchar( key & 0xff );
transx = transx + 1;
if( transx > 29 ) {
transx = 2;
transy++;
if( transy > 16 ) {
scrollup(1,2,28,15);
transy = 16;
}
}
}
}
else {
while( char_ready == TRUE ) {
recch = *outptr;
gotoxy( recx, recy );
if( overrun ) {
attribute |= 0x80;
putchar( 0x07 );
}
else attribute = 0x07;
writechar( recch, attribute );
++outptr;
if( outptr == inptr )
char_ready = FALSE;
if( outptr > buffer + maxsize )
outptr = buffer;
recx = recx + 1;
if( recx > 71 ) {
recx = 35;
recy++;
if( recy > 16 ) {
scrollup(34,2,71,15);
recy = 16;
}
}
}
}
}
rs232_deinstall();
}