Real Interrupts-Microprocessor and Assembly Language Programming-Lecture Notes, Study notes of Microprocessor and Assembly Language Programming

This lecture handout was provided at Quaid-i-Azam University for Microprocessor and Assembly Language Programming course by Prof. Saleem Raza. Its main points are: Language, Programmable, Interrpt, Controller, Generate, Signal, Ports, Handler, Keyboard, Chaining, Bios

Typology: Study notes

2011/2012

Uploaded on 08/04/2012

saqqi
saqqi 🇵🇰

4

(33)

40 documents

1 / 18

Toggle sidebar

This page cannot be seen from the preview

Don't miss anything!

bg1
The same mechanism as discussed in the previous chapter is used for real
interrupts that are generated by external hardware. However there is a single
pin outside the processor called the INT pin that is used by external
hardware to generate interrupts. The detailed operation that happens outside
the process when an interrupt is generated is complex and only a simplified
view will be discussed here; the view that is relevant to an assembly language
programmer. There are many external devices that need the processor’s
attention like the keyboard, hard disk, floppy disk, sound card. All of them
need real time interrupts at some point in their operation. For example if a
program is busy in some calculations for three minutes the key strokes that
are hit meanwhile should not be wasted. Therefore when a key is pressed,
the INT signal is sent, an interrupt generated and the interrupt handler
stores the key for later use. Similarly when the printer is busy printing we
cannot send it more data. As soon as it gets free from the previous job it
interrupts the processor to inform that it is free now. There are many other
examples where the processor needs to be informed of an external event. If
the processor actively monitors all devices instead of being automatically
interrupted then it there won’t be any time to do meaningful work.
Since there are many devices generating interrupts and there is only one
pin going inside the processor and one pin cannot be technically derived by
more than one source a controller is used in between called the
Programmable Interrupt Controller (PIC). It has eight input signals and one
output signal. It assigns priorities to its eight input pins from 0 to 7 so that if
more than one interrupt comes at the same times, the highest priority one is
forwarded and the rest are held till that is serviced. The rest are forwarded
one by one according to priority after the highest priority one is completed.
The original IBM XT computer had one PIC so there were 8 possible interrupt
sources. However IBM AT and later computers have two PIC totaling 16
possible interrupt sources. They are arrange is a special cascade master
slave arrangement so that only one output signal comes towards the
processor. However we will concentrate on the first interrupt controller only.
The priority can be understood with the following example. Consider eight
parallel switches which are all closed and connected to form the output
signal. When a signal comes on one of the switches, it is passed on to the
output and this switch and all below it are opened so that no further signals
can pass through it. The higher priority switches are still closed and the
signal on them can be forwarded. When the processor signals that it is
finished with the processing the switches are closed again and any waiting
interrupts may be forwarded. The way the processor signals ending of the
interrupt service routine is by using a special mechanism discussed later.
The eight input signals to the PIC are called Interrupt Requests (IRQ). The
eight lines are called IRQ 0 to IRQ 7. These are the input lines of the 8451.
For example IRQ 0 is derived by a timer device. The timer device keeps
8259 is the technical number of the PIC.
pf3
pf4
pf5
pf8
pf9
pfa
pfd
pfe
pff
pf12

Partial preview of the text

Download Real Interrupts-Microprocessor and Assembly Language Programming-Lecture Notes and more Study notes Microprocessor and Assembly Language Programming in PDF only on Docsity!

The same mechanism as discussed in the previous chapter is used for real

interrupts that are generated by external hardware. However there is a single

pin outside the processor called the INT pin that is used by external

hardware to generate interrupts. The detailed operation that happens outside

the process when an interrupt is generated is complex and only a simplified

view will be discussed here; the view that is relevant to an assembly language

programmer. There are many external devices that need the processor’s

attention like the keyboard, hard disk, floppy disk, sound card. All of them

need real time interrupts at some point in their operation. For example if a

program is busy in some calculations for three minutes the key strokes that

are hit meanwhile should not be wasted. Therefore when a key is pressed,

the INT signal is sent, an interrupt generated and the interrupt handler

stores the key for later use. Similarly when the printer is busy printing we

cannot send it more data. As soon as it gets free from the previous job it

interrupts the processor to inform that it is free now. There are many other

examples where the processor needs to be informed of an external event. If

the processor actively monitors all devices instead of being automatically

interrupted then it there won’t be any time to do meaningful work.

Since there are many devices generating interrupts and there is only one

pin going inside the processor and one pin cannot be technically derived by

more than one source a controller is used in between called the

Programmable Interrupt Controller (PIC). It has eight input signals and one

output signal. It assigns priorities to its eight input pins from 0 to 7 so that if

more than one interrupt comes at the same times, the highest priority one is

forwarded and the rest are held till that is serviced. The rest are forwarded

one by one according to priority after the highest priority one is completed.

The original IBM XT computer had one PIC so there were 8 possible interrupt

sources. However IBM AT and later computers have two PIC totaling 16

possible interrupt sources. They are arrange is a special cascade master

slave arrangement so that only one output signal comes towards the

processor. However we will concentrate on the first interrupt controller only.

The priority can be understood with the following example. Consider eight

parallel switches which are all closed and connected to form the output

signal. When a signal comes on one of the switches, it is passed on to the

output and this switch and all below it are opened so that no further signals

can pass through it. The higher priority switches are still closed and the

signal on them can be forwarded. When the processor signals that it is

finished with the processing the switches are closed again and any waiting

interrupts may be forwarded. The way the processor signals ending of the

interrupt service routine is by using a special mechanism discussed later.

The eight input signals to the PIC are called Interrupt Requests (IRQ). The

eight lines are called IRQ 0 to IRQ 7. These are the input lines of the 8451. †

For example IRQ 0 is derived by a timer device. The timer device keeps

† 8259 is the technical number of the PIC.

generating interrupts with a specified frequency. IRQ 1 is derived by the

keyboard when generates an interrupts when a key is pressed or released.

IRQ 2 is the cascading interrupt connected to the output of the second 8451

in the machine. IRQ 3 is connected to serial port COM 2 while IRQ 4 is

connected to serial port COM 1. IRQ 5 is used by the sound card or the

network card or the modem. An IRQ conflict means that two devices in the

system want to use the same IRQ line. IRQ 6 is used by the floppy disk drive

while IRQ 7 is used by the parallel port.

Each IRQ is mapped to a specific interrupt in the system. This is called the

IRQ to INT mapping. IRQ 0 to IRQ 7 are consecutively mapped on interrupts

8 to F. This mapping is done by the PIC and not the processor. The actual

mechanism fetches one instruction from the PIC whenever the INT pin is

signaled instead of the memory. We can program the PIC to generate a

different set of interrupts on the same interrupt requests. From the

perspective of an assembly language programmer an IRQ 0 is translated into

an INT 8 without any such instruction in the program and that’s all.

Therefore an IRQ 0, the highest priority interrupt, is generated by the timer

chip at a precise frequency and the handler at INT 8 is invoked which

updates the system time. A key press generates IRQ 1 and the INT 9 handler

is invoked which stores this key. To handler the timer and keyboard

interrupts one can replace the vectors corresponding to interrupt 8 and 9

respectively. For example if the timer interrupt is replaced and the floppy is

accessed by some program, the floppy motor and its light will remain on for

ever as in the normal case it is turned off by the timer interrupt after two

seconds in anticipation that another floppy access might be needed

otherwise the time motor takes to speed up will be needed again. ‡

We have seen that an interrupt request from a device enters the PIC as an

IRQ, from there it reaches the INT pin of the processor, the processor

receives the interrupt number from the PIC, generates the designated

interrupt, and finally the interrupt handler gain control and can do whatever

is desired. At the end of servicing the interrupt the handler should inform the

PIC that it is completed so that lower priority interrupts can be sent from the

PIC. This signal is called an End Of Interrupt (EOI) signal and is sent

through the I/O ports of the interrupt controller.

I/O PORTS

There are hundreds of peripheral devices in the system, PIC is one

example. The processor needs to communicate with them, give and take data

from them, otherwise their presence is meaningless. Memory has a totally

different purpose. It contains the program to be executed and its data. It

does not control any hardware. For communicating with peripheral devices

the processor uses I/O ports. There are only two operations with the external

world possible, read or write. Similarly with I/O ports the processor can read

or write an I/O port. When an I/O port is read or written to, the operation is

not as simple as it happens in memory. Some hardware changes it

functionality or performs some operation as a result.

IBM PC has separate memory address space and peripheral address space.

Some processors use memory mapped I/O in which case designated memory

cells work as ports for specific devices. In case of Intel a special pin on the

control bus signals whether the current read or write is from the memory

address space or from the peripheral address space. The same address and

data buses are used to select a port and to read or write data from that port.

However with I/O only the lower 16 bits of the address bus are used meaning

that there are a total of 65536 possible I/O ports. Now keyboard has special

‡ The programs discussed from now onwards in the book must be executed

in pure DOS and not in a DOS window so that we are in total control of the

PIC and other devices.

controller communicates with the processor. Keyboard is a collection of

labeled buttons and every button is designated a number (not the ASCII

code). This number is sent to the processor whenever the key is pressed.

From this number called the scan code the processor understands which key

was pressed. For each key the scan code comes twice, once for the key press

and once for the key release. Both are scan codes and differ in one bit only.

The lower seven bits contain the key number while the most significant bit is

clear in the press code and set in the release code. The IBM PC standard

gives a table of the scan codes of all keys.

If we press Shift-A resulting in a capital A on the screen, the controller has

sent the press code of Shift, the press code of A, the release code of A, the

release code of Shift and the interrupt handler has understood that this

sequence should result in the ASCII code of ‘A’. The ‘A’ key always produces

the same scan code whether or not shift is pressed. It is the interrupt

handler’s job to remember that the press code of Shift has come and release

code has not yet come and therefore to change the meaning of the following

key presses. Even the caps lock key works the same way.

An interesting thing is that the two shift keys on the left and right side of

the keyboard produce different scan codes. The standard way implemented

in BIOS is to treat that similarly. That’s why we always think of them as

identical. If we leave BIOS and talk directly with the hardware we can

differentiate between left and right shift keys with their scan code. Now this

scan code is available from the keyboard data port which is 60. The keyboard

generates IRQ 1 whenever a key is pressed so if we hook INT 9 and inside it

read port 60 we can tell which of the shift keys was hit. Our first program

will do precisely this. It will output an L if the left shift key was pressed and

R if the right one was pressed. The hooking method is the same as done in

the previous chapter.

Example

; differentiate left and right shift keys with scancodes [org 0x0100] jmp start

; keyboard interrupt service routine kbisr: push ax push es

mov ax, 0xb mov es, ax ; point es to video memory

in al, 0x60 ; read a char from keyboard port cmp al, 0x2a ; is the key left shift jne nextcmp ; no, try next comparison

mov byte [es:0], 'L' ; yes, print L at top left jmp nomatch ; leave interrupt routine

nextcmp: cmp al, 0x36 ; is the key right shift jne nomatch ; no, leave interrupt routine

mov byte [es:0], 'R' ; yes, print R at top left

nomatch: mov al, 0x out 0x20, al ; send EOI to PIC

pop es pop ax iret

start: xor ax, ax mov es, ax ; point es to IVT base cli ; disable interrupts mov word [es:94], kbisr ; store offset at n mov [es:94+2], cs ; store segment at n4+ sti ; enable interrupts

038 l1: jmp l1 ; infinite loop

033-036 CLI clears the interrupt flag to disable the interrupt system

completely. The processor closes its ears and does not care about

the state of the INT pin. Interrupt hooking is done in two

instructions, placing the segment and placing the offset. If an

interrupt comes in between and the vector is in an indeterminate

state, the system will go to a junk address and eventually crash. So

we stop all interruptions while changing a real time interrupt vector.

We set the interrupt flag afterwards to renewable interrupts.

038 The program hangs in an infinite loop. The only activity can be

caused by a real time interrupt. The kbisr routine is not called from

anywhere; it is only automatically invoked as a result of IRQ 1.

When the program is executed the left and right shift keys can be

distinguished with the L or R on the screen. As no action was taken for the

rest of the keys, they are effectively disabled and the computer has to be

rebooted. To check that the keyboard is actually disabled we change the

program and add the INT 16 service 0 at the end to wait for an Esc key press.

As soon as Esc is pressed we want to terminate our program.

Example

; attempt to terminate program with Esc that hooks keyboard interrupt [org 0x0100] jmp start

;;;;; COPY LINES 005-029 FROM EXAMPLE 9.2 (kbisr) ;;;;;

start: xor ax, ax mov es, ax ; point es to IVT base cli ; disable interrupts mov word [es:94], kbisr ; store offset at n mov [es:94+2], cs ; store segment at n4+ sti ; enable interrupts

l1: mov ah, 0 ; service 0 – get keystroke int 0x16 ; call BIOS keyboard service

cmp al, 27 ; is the Esc key pressed jne l1 ; if no, check for next key

mov ax, 0x4c00 ; terminate program int 0x

When the program is executed the behavior is same. Esc does not work.

This is because the original IRQ 1 handler was written by BIOS that read the

scan code, converted into an ASCII code and stored in the keyboard buffer.

The BIOS INT 16 read the key from there and gives in AL. When we hooked

the keyboard interrupt BIOS is no longer in control, it has no information, it

will always see the empty buffer and INT 16 will never return.

Interrupt Chaining

We can transfer control to the original BIOS ISR in the end of our routine.

This way the normal functioning of INT 16 can work as well. We can retrieve

the address of the BIOS routine by saving the values in vector 9 before

hooking our routine. In the end of our routine we will jump to this address

using a special indirect form of the JMP FAR instruction.

Example

memory as a result. The routine is still in memory and functioning but the

memory is free according to DOS. As soon as we load EDIT the same memory

is allocated to EDIT and our routine as overwritten. Now when a key is

pressed our routine’s address is in the vector but at that address some new

code is placed that is not intended to be an interrupt handler. That may be

data or some part of the EDIT program. This results in crashing the

computer.

Unhooking Interrupt

We now add the interrupt restoring part to our program. This code resets

the interrupt vector to the value it had before the start of our program.

Example

; terminate program with Esc that hooks keyboard interrupt [org 0x100] jmp start

oldisr: dd 0 ; space for saving old isr

;;;;; COPY LINES 005-029 FROM EXAMPLE 9.4 (kbisr) ;;;;;

start: xor ax, ax mov es, ax ; point es to IVT base mov ax, [es:94] mov [oldisr], ax ; save offset of old routine mov ax, [es:94+2] mov [oldisr+2], ax ; save segment of old routine cli ; disable interrupts mov word [es:94], kbisr ; store offset at n mov [es:94+2], cs ; store segment at n4+ sti ; enable interrupts

l1: mov ah, 0 ; service 0 – get keystroke int 0x16 ; call BIOS keyboard service

cmp al, 27 ; is the Esc key pressed jne l1 ; if no, check for next key

mov ax, [oldisr] ; read old offset in ax mov bx, [oldisr+2] ; read old segment in bx cli ; disable interrupts mov [es:94], ax ; restore old offset from ax mov [es:94+2], bx ; restore old segment from bx sti ; enable interrupts

mov ax, 0x4c00 ; terminate program int 0x

. TERMINATE AND STAY RESIDENT

We change the display to show L only while the left shift is pressed and R

only while the right shift is pressed to show the use of the release codes. We

also changed that shift keys are not forwarded to BIOS. The effect will be

visible with A and Shift-A both producing small ‘a’ but caps lock will work.

There is one major difference from all the programs we have been writing

till now. The termination is done using INT 21 service 31 instead of INT 21

service 4C. The effect is that even after termination the program is there and

is legally there.

Example

; TSR to show status of shift keys on top left of screen [org 0x0100] jmp start

oldisr: dd 0 ; space for saving old isr

; keyboard interrupt service routine kbisr: push ax push es

mov ax, 0xb mov es, ax ; point es to video memory

in al, 0x60 ; read a char from keyboard port cmp al, 0x2a ; has the left shift pressed jne nextcmp ; no, try next comparison

mov byte [es:0], 'L' ; yes, print L at first column jmp exit ; leave interrupt routine

nextcmp: cmp al, 0x36 ; has the right shift pressed jne nextcmp2 ; no, try next comparison

mov byte [es:0], 'R' ; yes, print R at second column jmp exit ; leave interrupt routine

nextcmp2: cmp al, 0xaa ; has the left shift released jne nextcmp3 ; no, try next comparison

mov byte [es:0], ' ' ; yes, clear the first column jmp exit ; leave interrupt routine

nextcmp3: cmp al, 0xb6 ; has the right shift released jne nomatch ; no, chain to old ISR

mov byte [es:2], ' ' ; yes, clear the second column jmp exit ; leave interrupt routine

nomatch: pop es pop ax jmp far [cs:oldisr] ; call the original ISR

exit: mov al, 0x out 0x20, al ; send EOI to PIC

pop es pop ax iret ; return from interrupt

start: xor ax, ax mov es, ax ; point es to IVT base mov ax, [es:94] mov [oldisr], ax ; save offset of old routine mov ax, [es:94+2] mov [oldisr+2], ax ; save segment of old routine cli ; disable interrupts mov word [es:94], kbisr ; store offset at n mov [es:94+2], cs ; store segment at n4+ sti ; enable interrupts

mov dx, start ; end of resident portion add dx, 15 ; round up to next para mov cl, 4 shr dx, cl ; number of paras mov ax, 0x3100 ; terminate and stay resident int 0x

When this program is executed the command prompt immediately comes.

DIR can be seen. EDIT can run and keypresses do not result in a crash. And

with all that left and right shift keys shown L and R on top left of the screen

while they are pressed but the shift keys do not work as usual since we did

not forwarded the key to BIOS. This is selective chaining.

This command displays all currently loaded drivers and the current state

of memory. We will be able to see our program in the list of DOS drivers.

PROGRAMMABLE INTERVAL TIMER

Another very important peripheral device is the Programmable Interval

Timer (PIT), the chip numbered 8254. This chip has a precise input

frequency of 1.19318 MHz. This frequency is fixed regardless of the processor

clock. Inside the chip is a 16bit divisor which divides this input frequency

and the output is connected to the IRQ 0 line of the PIC. The special number

0 if placed in the divisor means a divisor of 65536 and not 0. The standard

divisor is 0 unless we change it. Therefore by default IRQ 0 is generated

1193180/65536=18.2 times per second. This is called the timer tick. There is

an interval of about 55ms between two timer ticks. The system time is

maintained with the timer interrupt. This is the highest priority interrupt

and breaks whatever is executing. Time can be maintained with this

interrupt as this frequency is very precise and is part of the IBM standard.

When writing a TSR we give control back to DOS so TSR activation,

reactivation and action is solely interrupt based, whether this is a hardware

interrupt or a software one. Control is never given back; it must be caught,

just like we caught control by hooking the keyboard interrupt. Our next

example will hook the timer interrupt and display a tick count on the screen.

Example

; display a tick count on the top right of screen [org 0x0100] jmp start

tickcount: dw 0

; subroutine to print a number at top left of screen ; takes the number to be printed as its parameter printnum: push bp mov bp, sp push es push ax push bx push cx push dx push di

mov ax, 0xb mov es, ax ; point es to video base mov ax, [bp+4] ; load number in ax mov bx, 10 ; use base 10 for division mov cx, 0 ; initialize count of digits

nextdigit: mov dx, 0 ; zero upper half of dividend div bx ; divide by 10 add dl, 0x30 ; convert digit into ascii value push dx ; save ascii value on stack inc cx ; increment count of values cmp ax, 0 ; is the quotient zero jnz nextdigit ; if no divide it again

mov di, 140 ; point di to 70th column

nextpos: pop dx ; remove a digit from the stack mov dh, 0x07 ; use normal attribute mov [es:di], dx ; print char on screen add di, 2 ; move to next screen location loop nextpos ; repeat for all digits on stack

pop di pop dx pop cx pop bx pop ax

pop es pop bp ret 2

; timer interrupt service routine timer: push ax

inc word [cs:tickcount]; increment tick count push word [cs:tickcount] call printnum ; print tick count

mov al, 0x out 0x20, al ; end of interrupt

pop ax iret ; return from interrupt

start: xor ax, ax mov es, ax ; point es to IVT base cli ; disable interrupts mov word [es:84], timer; store offset at n mov [es:84+2], cs ; store segment at n4+ sti ; enable interrupts

mov dx, start ; end of resident portion add dx, 15 ; round up to next para mov cl, 4 shr dx, cl ; number of paras mov ax, 0x3100 ; terminate and stay resident int 0x

When we execute the program the counter starts on the screen. Whatever

we do, take directory, open EDIT, the debugger etc. the counter remains

running on the screen. No one is giving control to the program; the program

is getting executed as a result of timer generating INT 8 after every 55ms.

Our next example will hook both the keyboard and timer interrupts. When

the shift key is pressed the tick count starts incrementing and as soon as the

shift key is released the tick count stops. Both interrupt handlers are

communicating through a common variable. The keyboard interrupt sets this

variable while the timer interrupts modifies its behavior according to this

variable.

Example

; display a tick count while the left shift key is down [org 0x0100] jmp start

seconds: dw 0 timerflag: dw 0 oldkb: dd 0

;;;;; COPY LINES 007-047 FROM EXAMPLE 9.7 (printnum) ;;;;;

; keyboard interrupt service routine kbisr: push ax

in al, 0x60 ; read char from keyboard port cmp al, 0x2a ; has the left shift pressed jne nextcmp ; no, try next comparison

cmp word [cs:timerflag], 1; is the flag already set je exit ; yes, leave the ISR

mov word [cs:timerflag], 1; set flag to start printing jmp exit ; leave the ISR

nextcmp: cmp al, 0xaa ; has the left shift released jne nomatch ; no, chain to old ISR

mov word [cs:timerflag], 0; reset flag to stop printing

ports through which the processor communicates with the device connected

to the parallel port.

The parallel port connector is a 25pin connector called DB-25. Different

pins of this connector have different meanings. Some are meaningful only

with the printer **^. This is a bidirectional port so there are some pins to take

data from the processor to the parallel port and others to take data from the

parallel port to the processor. Important pins for our use are the data pins

from pin 2 to pin 9 that take data from the processor to the device. Pin 10,

the ACK pin, is normally used by the printer to acknowledge the receipt of

data and show the willingness to receive more data. Signaling this pin

generates IRQ 7 if enabled in the PIC and in the parallel port controller. Pin

18-25 are ground and must be connected to the external circuit ground to

provide the common reference point otherwise they won’t understand each

other voltage levels. Like the datum point in a graph this is the datum point

of an electrical circuit. The remaining pins are not of our concern in these

examples.

This is the external view of the parallel port. The processor cannot see

these pins. The processor uses the I/O ports of the parallel port controller.

The first parallel port LPT1 ††^ has ports designated from 378 to 37A. The first

port 378 is the data port. If we use the OUT instruction on this port, 1 bits

result in a 5V signal on the corresponding pin and a 0 bits result in a 0V

signal on the corresponding pin.

Port 37A is the control port. Our interest is with bit 4 of this port which

enables the IRQ 7 triggering by the ACK pin. We have attached a circuit that

connects 8 LEDs with the parallel port pins. The following examples sends

the scancode of the key pressed to the parallel port so that it is visible on

LEDs.

Example

; show scancode on external LEDs connected through parallel port [org 0x0100] jmp start

oldisr: dd 0 ; space for saving old ISR

; keyboard interrupt service routine kbisr: push ax push dx

in al, 0x60 ; read char from keyboard port mov dx, 0x out dx, al ; write char to parallel port

pop ax pop dx jmp far [cs:oldisr] ; call original ISR

start: xor ax, ax mov es, ax ; point es to IVT base mov ax, [es:94] mov [oldisr], ax ; save offset of old routine mov ax, [es:94+2] mov [oldisr+2], ax ; save segment of old routine cli ; disable interrupts mov word [es:94], kbisr ; store offset at n mov [es:94+2], cs ; store segment at n4+ sti ; enable interrupts

mov dx, start ; end of resident portion add dx, 15 ; round up to next para

** The parallel port is most commonly used with the printer. However some

new printers have started using the USB port.

†† Older computer had more than one parallel port named LPT2 and having

ports from 278-27A.

mov cl, 4 shr dx, cl ; number of paras mov ax, 0x3100 ; terminate and stay resident int 0x

The following example uses the same LED circuit and sends data such that

LEDs switch on and off turn by turn so that it looks like light is moving back

and forth.

Example

; show lights moving back and forth on external LEDs [org 0x0100] jmp start

signal: db 1 ; current state of lights direction: db 0 ; current direction of motion

; timer interrupt service routine timer: push ax push dx push ds

push cs pop ds ; initialize ds to data segment

cmp byte [direction], 1; are moving in right direction je moveright ; yes, go to shift right code

shl byte [signal], 1 ; shift left state of lights jnc output ; no jump to change direction

mov byte [direction], 1; change direction to right mov byte [signal], 0x80; turn on left most light jmp output ; proceed to send signal

moveright: shr byte [signal], 1 ; shift right state of lights jnc output ; no jump to change direction

mov byte [direction], 0; change direction to left mov byte [signal], 1 ; turn on right most light

output: mov al, [signal] ; load lights state in al mov dx, 0x378 ; parallel port data port out dx, al ; send light state of port

mov al, 0x out 0x20, al ; send EOI on PIC

pop ds pop dx pop ax iret ; return from interrupt

start: xor ax, ax mov es, ax ; point es to IVT base cli ; disable interrupts mov word [es:84], timer ; store offset at n mov [es:84+2], cs ; store segment at n4+ sti ; enable interrupts

mov dx, start ; end of resident portion add dx, 15 ; round up to next para mov cl, 4 shr dx, cl ; number of paras mov ax, 0x3100 ; terminate and stay resident int 0x

We will now use the parallel port to control a slightly complicated circuit.

This time we will also use the parallel port interrupt. We are using a 220 V

bulb with AC input. AC current is 50Hz sine wave. We have made our circuit

such that it triggers the parallel port interrupt whenever the since wave

start: xor ax, ax mov es, ax ; point es to IVT base mov ax, [es:0x084] mov [oldtimer], ax ; save offset of old routine mov ax, [es:0x084+2] mov [oldtimer+2], ax ; save segment of old routine cli ; disable interrupts mov word [es:0x084], timer ; store offset at n mov [es:0x084+2], cs ; store segment at n4+ mov word [es:0x0F4], parallel ; store offset at n mov [es:0x0F4+2], cs ; store segment at n4+ sti ; enable interrupts

mov dx, 0x37A in al, dx ; parallel port control register or al, 0x10 ; turn interrupt enable bit on out dx, al ; write back register

in al, 0x21 ; read interrupt mask register and al, 0x7F ; enable IRQ7 for parallel port out 0x21, al ; write back register

recheck: cmp byte [stop], 1 ; is the termination flag set jne recheck ; no, check again

mov dx, 0x37A in al, dx ; parallel port control register and al, 0xEF ; turn interrupt enable bit off out dx, al ; write back register

in al, 0x21 ; read interrupt mask register or al, 0x80 ; disable IRQ7 for parallel port out 0x21, al ; write back regsiter

cli ; disable interrupts mov ax, [oldtimer] ; read old timer ISR offset mov [es:0x084], ax ; restore old timer ISR offset mov ax, [oldtimer+2] ; read old timer ISR segment mov [es:0x084+2], ax ; restore old timer ISR segment sti ; enable interrupts

mov ax, 0x4c00 ; terminate program int 0x

The next example is simply the opposite of the previous. It slowly turns the

bulb off from maximum glow to no glow.

Example

; slowly turn off a bulb by gradually decreasing the power provided [org 0x0100] jmp start

flag: db 0 ; next time turn on or turn off stop: db 0 ; flag to terminate the program divider: dw 0 ; divider for maximum intensity oldtimer: dd 0 ; space for saving old isr

;;;;; COPY LINES 009-036 FROM EXAMPLE 9.11 (timer) ;;;;;

; parallel port interrupt service routine parallel: push ax

mov al, 0x30 ; set timer to one shot mode out 0x43, al

cmp word [cs:divider], 11000; current divisor is 11000 je stopit ; yes, stop

add word [cs:divider], 10; increase the divisor by 10 mov ax, [cs:divider] out 0x40, al ; load divisor LSB in timer mov al, ah out 0x40, al ; load divisor MSB in timer

mov byte [cs:flag], 1 ; flag next timer to switch on

mov al, 0x out 0x20, al ; send EOI to PIC pop ax iret ; return from interrupt

stopit: mov byte [stop], 1 ; flag to terminate the program mov al, 0x out 0x20, al ; send EOI to PIC pop ax iret ; return from interrupt

start: xor ax, ax mov es, ax ; point es to IVT base mov ax, [es:0x084] mov [oldtimer], ax ; save offset of old routine mov ax, [es:0x084+2] mov [oldtimer+2], ax ; save segment of old routine cli ; disable interrupts mov word [es:0x084], timer ; store offset at n mov [es:0x084+2], cs ; store segment at n4+ mov word [es:0x0F4], parallel ; store offset at n mov [es:0x0F4+2], cs ; store segment at n4+ sti ; enable interrupts

mov dx, 0x37A in al, dx ; parallel port control register or al, 0x10 ; turn interrupt enable bit on out dx, al ; write back register

in al, 0x21 ; read interrupt mask register and al, 0x7F ; enable IRQ7 for parallel port out 0x21, al ; write back register

recheck: cmp byte [stop], 1 ; is the termination flag set jne recheck ; no, check again

mov dx, 0x37A in al, dx ; parallel port control register and al, 0xEF ; turn interrupt enable bit off out dx, al ; write back register

in al, 0x21 ; read interrupt mask register or al, 0x80 ; disable IRQ7 for parallel port out 0x21, al ; write back regsiter

cli ; disable interrupts mov ax, [oldtimer] ; read old timer ISR offset mov [es:0x084], ax ; restore old timer ISR offset mov ax, [oldtimer+2] ; read old timer ISR segment mov [es:0x084+2], ax ; restore old timer ISR segment sti ; enable interrupts

mov ax, 0x4c00 ; terminate program int 0x

This example is a mix of the previous two. Here we can increase the bulb

intensity with F11 and decrease it with F12.

Example

; control external bulb intensity with F11 and F [org 0x0100] jmp start

flag: db 0 ; next time turn on or turn off divider: dw 100 ; initial timer divider oldkb: dd 0 ; space for saving old ISR

;;;;; COPY LINES 009-036 FROM EXAMPLE 9.11 (timer) ;;;;;

; keyboard interrupt service routine kbisr: push ax