Scrigroup - Documente si articole

     

HomeDocumenteUploadResurseAlte limbi doc
BulgaraCeha slovacaCroataEnglezaEstonaFinlandezaFranceza
GermanaItalianaLetonaLituanianaMaghiaraOlandezaPoloneza
SarbaSlovenaSpaniolaSuedezaTurcaUcraineana

AdministrationAnimalsArtBiologyBooksBotanicsBusinessCars
ChemistryComputersComunicationsConstructionEcologyEconomyEducationElectronics
EngineeringEntertainmentFinancialFishingGamesGeographyGrammarHealth
HistoryHuman-resourcesLegislationLiteratureManagementsManualsMarketingMathematic
MedicinesMovieMusicNutritionPersonalitiesPhysicPoliticalPsychology
RecipesSociologySoftwareSportsTechnicalTourismVarious

Parallel and Serial Interfacing - PARALLEL PORTS

computers



+ Font mai mare | - Font mai mic



Parallel and Serial Interfacing

In order to create effective graphics software, you must provide

ways for your computer to communicate with the outside world.

Your software can communicate with the user through the use of



CRT displays, printers, plotters, mice, digitizers, light pens,

and a host of other devices. These devices are connected to the

computer in various ways.

This chapter is about the art and science of interfacing. This

subject pertains to all aspects of computer use, not just

graphics. You will find that the skills required for competent

graphics programming embrace all other computer skills. In other

words, if you can create graphics using a computer, you will be

able to do almost any other kind of computer programming.

You might think that interfacing is boring, that it has little

to do with creativity. You will find, however, that interfacing

of peripherals can be done in a variety of ways. Depending on

your choices, you can create magic or end up with software that

balks at everything. All graphics programs rely on successful

interfacing techniques.

When you are working with locator devices, you will need to

receive data from the serial interface. Traditionally, because of

the ineffectiveness of the parallel interface on the IBM PC, the

serial interface is most often used with locator devices. To

receive data you can use serial interrupts, or you can use a

polled interface method. The polled method, which involves using

a polled serial input protocol to ask the sending device for each

byte as it arrives, is the easier one to use.

The code presented in this chapter is intentionally explicit

rather than containing included files and mnemonics. It is

intended to help you understand what is going on in the

functions, rather than serving as an example of code you would

use in a program. You may find that many explicit references

could be made using mnemonics or equates. By all means, feel free

to change the code to reflect your own style. Just be sure that

any changes you make accomplish the same things as the original

code would do.

Throughout this book you have seen interfacing used implicitly

in various functions. No detailed explanation of interface

options was offered. In this chapter you will see how to receive

information from digitizers, mice, and light pens. You will see

how serial and parallel interface work, not only for graphics but

for all of your software. Finally, you will see a serial

interrupt service routine that really works.

PARALLEL PORTS

The parallel interface port on the IBM PC series of computers is

the easiest interface to understand. Most devices that connect to

it are wired at the factory to be compatible. You seldom need to

worry about making cables for parallel ports.

The parallel port is used primarily for interfacing with

printers. It uses eight data wires, one for each bit in a byte to

be sent. The use of eight wires means that eight bits can be sent

at once, in parallel. The serial interface would require you to

send the same eight bits one bit at a time.

Sending Characters Through a Parallel Port

To send a character through the parallel port, you first need to

determine which port you will use. You then use either the BIOS

or direct access to the port registers to send the character.

Because the BIOS in this instance is perfectly adequate, a BIOS

interrupt is the preferred way to send characters through the

parallel port.

To send a character you use BIOS interrupt 17h. Before

executing the interrupt, you load AH with 0, load AL with the

character to be printed, and load DX with the desired printer

number. Printer numbers 0, 1, and 2 correspond with LPT1, LPT2,

and LPT3, respectively.

.pa

Monitoring Printer Status

You must also be concerned with the status of the printer. If the

printer cannot receive the character you wish to send, it will

raise or lower one or more of its status wires. The status wires

are monitored by setting AH to 2 and DX to the desired printer

number. The printer status is returned in AH after you execute

interrupt 17h. In practice, you first generate interrupt 17h with

AH set to 2 and loop until AH returns a nonzero value for bit 8.

When this occurs, you know that the printer is not busy and you

send the desired character. This is all the handshaking you will

ever need for parallel output.

Function 11-1, PUT_OUT.C, shows a complete character output

function that you can use for any software you write. It is very

clean, and you seldom need more that this. You will encounter it

in GRAPHIQ.C in Appendix A in the code used for dithering

pictures out onto the dot-matrix printer.

FUNCTION 11 - 1

-------- ----- ------ ----- ----- --------- ----- --------

PUT_OUT.C

-------- ----- ------ ----- ----- --------- ----- --------

/* PUT_OUT.C puts a character out the parallel

** port using the BIOS.

*/

char put_out(character)

char character;

char status()

Parallel Output Without Using DOS or the BIOS

To go one level further down, you can use the technique shown in

Function 11-2, PRALEL_O.C, to do almost the same thing the BIOS

does, but from the C language. Use this function if you wish to

change low-level access for any reason.

Normally, the standard for parallel interfacing requires 36

pins. IBM wanted to use a 25-pin D connector (a female), leaving

11 pins unused. The idea was to use a male 25-pin D connector for

the serial interface and a female connector between the two

without taking the cover off the computer. This strategy has

backfired because users often plug serial devices into parallel

ports the serial devices happen to have male connectors. This

puts +_12 volts on TTL (transistor-transistor logic) circuits

capable of sinking 0 through 5 volts, and has probably destroyed

many parallel adapter.

Of course, one implication of the unfortunate choice of

connectors is that all the pins necessary to support the full

parallel standard are not available. In its infinite wisdom, IBM

chose to make the parallel BIOS interface capable of only output!

If they had used a 36-pin connector, the means of supporting full

bidirectional input/output could have been parallel.

Three parallel adapters are recognized by the BIOS. Their

addresses are stored starting at 0000:0408 in RAM. The best way

to find the address of the port for a given adapter is to use

word offsets from base 0000:0408, as follows:

unsigned far *base = 0x00000408L + paraport;

The long variable paraport is 0 for LPT1, 2 for LPT2, or 4 for

LPT3. This can be expressed in definitions as follows:

#define PARALLEL2 0L

#define PARALLEL2 2L

#define PARALLEL3 4L

FUNCTION 11-2

-------- ----- ------ ----- ----- --------- ----- -------

PRALEL_O.C

-------- ----- ------ ----- ----- --------- ----- -------

/* PRALEL_O.C uses a non-BIOS method to send a

** character out the parallel port.

** This method uses no timeout checking as

** the BIOS does.

*/

/* possible values for paraport */

#define PARALLEL1 0L

#define PARALLEL2 2L

#define PARALLEL3 4L

unsigned far *base = 0x00000408L;

char stat_o();

char pralel_o(character, paraport)

char character;

long paraport;

char stat_o(paraport)

long paraport;

Note that LPT1, LPT2, and LPT3 are reserved DOS device names,

and so it is unwise to use them as they are in your programs,

unless you wish to refer to devices DOS recognizes.

LPT1 can also appear as PRN. You can send or receive bytes from

the parallel port by using the OUT or IN instructions of the

microprocessor. A typical output to the eight-bit parallel latch

can be executed as follows:

outp(*base, character);

This assumes that you have initialized base to point to the

address of the desired parallel port. The only other

consideration is that you must be able to tell the sending device

when to send the next character. To do this you control the same

pins that the BIOS monitors when receiving a character.

One thing Function 11-2 does not do is repeatedly test port if

the character was not successfully sent. This means that there is

no certainty that the receiving device is working. In practice,

this usually does not create problems. You can avoid testing for

device timeout in this way.

Receiving Characters from a Parallel Port

There is no support in the BIOS for receiving characters using

the parallel interface. This is truly unfortunate. If most

plotters, digitizers, and other devices used the parallel

interface rather than the serial interface, there would be much

less confusion in the computer industry. Further, the parallel

interface is potentially much faster and more reliable than the

RS232C serial interface. The only drawback would be the fact that

cables for parallel interfaces cannot be as long as serial

cables. The voltages on parallel interface wires range from 0 to

+5, relying on ground potential for their off condition. The

serial interface uses voltages lower than -3 volts or greater

than +3 volts to determine the on or off stare of the interface

wire. The usual voltage employed in serial interfaces is +_12

volts. The use of ground potential and a narrowly defined +5-volt

signal level makes parallel input/output more susceptible to

electrical noise than serial input/output.

Because most plotters and digitizers use cables that are less

than 8 feet long, the use of parallel, rather than serial,

interfacing for these devices would have been possible.

Traditionally, though, this was just not done. It is probably a

tragic case of false economy. Is one interface wire really

cheaper than eight? Looking backward, with the benefit of

hindsight, it probably would have been better to use serial

interfacing with modems only for telephone communications (where

one wire is all you get) and full 36-wire parallel interfacing

for everything else.

Receiving a character through the parallel port is a more

difficult task than sending one. To receive a character, you must

violate the usage ground rules of the parallel interface

standard. Normally, receiving would be done using pins 20 through

27 of the 36-pin standard for data. This would enable received

data to be latched simultaneously with sent data. However, the

kludged design of the IBM parallel interface makes this

impossible.

Fortunately, although it is nonstandard, you can use the eight

pins normally used for output to receive as well as send a byte.

Of course, you cannot receive and sent at the same time, as you

can with the full 36-pin standard, but you can multiplex the

interface to share the same eight pins, with some reduction in

speed and improvisation of handshaking.

Function 11-3, PRALEL_I.C, is like Function 11-2 except that

it receives rather than sends a character. You may need to modify

Function 11-3 to meet the needs of specific sending devices. For

example, a specific device may need a handshake using the strobe

wire (pin 1). You cannot send acknowledge or busy signals,

unfortunately, because the status port is meant to be read only.

Also, you should set the bits of the status register that are not

used to states that are required by the sending device. In other

words, you must be careful to meet the requirements of the

sending device, using a modified form of Function 11-3.

FUNCTION 11-3

-------- ----- ------ ----- ----- --------- ----- -------

PRALEL_I.C

-------- ----- ------ ----- ----- --------- ----- -------

/* PRALEL_I.C uses a non-BIOS method to receive a

** character from the parallel port.

** This is a simple handshake protocol that, although

** unable to receive a null byte, is very effective.

*/

/* possible values for paraport */

#define PARALLEL1 0L

#define PARALLEL2 2L

#define PARALLEL3 4L

unsigned far *base = 0x00000408L;

char stat_i();

char pralel_i(paraport)

long paraport;

Understand that Function 11-3 is intended only as a suggestion.

To make it work, you must provide initialization and handshaking

that are appropriate for the sending device.

The technique in Function 11-3 assumes that the sender waits

for all bits of data to be 0 before sending the next character.

Because the handshake is based on setting all data bits to 0, you

cannot receive a null byte. For most purposes this is adequate,

but it depends on the sender recognizing this protocol. The

parallel port is seldom used to receive data; you will probably

use it for this only when you wire your own hardware. This

protocol is suggested for those circumstances.

In order to use Function 11-3, you must create a receiver

buffer of as many characters as will be received in one port

access. You may read the port using Function 11-3 for each

character in the buffer, until you have received as many

characters as you wish.

If you plan to interface parallel equipment professionally and

can create your own hardware, it is wise to use a full 36-pin

standard adapter. The use of this standard is highly encouraged

with the hope that someday it will replace the IBM parallel

interface, to the great benefit of the computer industry.

The Parallel Interface at a Glance

Figure 11-1 shows the IBM parallel port in all its glory, with

the contents of all registers and their relationships to the

wires used to connect the port to external devices. The parallel

port used never again be a mystery.

The diagram shows the pins on the IBM PC connector (female) as

well as the three registers used to establish communication with

the pins. Both the data register and the interrupt enable and

strobe register can be read or written. The status register will

show the current status of the wires, but you cannot use it to

change the status of the wires. The fact that the status register

is passive raised serious objections when the PC was first

marketed. Some PCs may permit handshaking using the status

register. Another way to establish a handshake is to do so using

null data, because null data bytes are seldom sent. This

technique will be discussed later.

Caution: Inverted Pin Status

Note that pins 17, 10, and 0 are inverted when represented in the

interrupt enable and strobe register. This can be very confusing

for people writing drivers because these wires are off when their

bit representations are on and vice versa. It is usually a

mistake to invert logic arbitrarily in designing interfaces, as

has been done in the BIOS.

FIGURE 11-1. The parallel port system at a glance

SERIAL PORTS

The serial interface on IBM PCs is much more versatile than the

parallel interface. Unlike the parallel interface, input as well

as output capabilities are included by design. But the BIOS,

unfortunately, has incredibly poor support for serial

input/output. You can use send characters, but that is about all.

If you wish to receive characters, the BIOS will just wait

forever, unless you happen to use the hardwire handshaking that

the BIOS anticipates. It is best to ignore the BIOS altogether

when working with serial communications.

Asynchronous Communications

The standard for serial data transfer (if something so confusing

can be called a standard) is called asynchronous because two

separate clocks are used, rather than a synchronization wire and

one clock. The two clocks (one for the transmitter and one for

the receiver) are never exactly in step, but their frequencies

are close enough so that the start and stop bits that frame a

word can be used to synchronize the reading of the bits in the

word.

The two clocks in an asynchronous data exchange are almost the

same frequency. Framed between the start bit and the final stop

bit, your data is clocked using the local clock and the known

start bit. The clock gets a little out of synchronization by the

time it reaches the last stop bit. If it is too far out of step,

it generates a framing error. A framing error indicates that the

difference between the clock of the receiver and the clock of the

sender is too great.

The Serial Port Registers

The IBM PC supports full interrupt handling for two serial ports

and leaves space in the table of interrupts for two more. You

will seldom require more than two serial ports (one for a mouse

or digitizer and one for a plotter), and so this is an adequate

arrangement.

Serial Adapter Base Addresses The addresses of each installed

communications adapter can be found in low memory starting at

0000:0400, in 16-bit words. The high byte of the address of the

first adapter is found at 0000:0401, and the low byte is found at

0000:0400. As elsewhere in this book, you can use a far pointer

to retrieve the contents of the word at this address as follows:

unsigned far *base = (unsigned far *)0x00000400L;

base_reg = *base;

The above method will transfer the port address to base_reg,

assuming that base_reg is type unsigned int. The actual address

of COM1 on IBM PCs is 0x3F8. Try using DEBUG to verify this for

yourself by dumping the contents of 0000:0400. It is better to

refer to the base_reg value indirectly, using the far pointer,

than to hardwire the port address. This way address can be

changed and your software will still work.

If you have two ports, the base address for COM2 will be

stored at 0000:0402. If three or four ports are available, the

addresses for the third and fourth will be at 0000:0404 and

0000:0406, respectively.

Starting at the base address for the communications adapter,

the addresses of the six registers associated with each port are

found by adding their offsets to base. Figure 11-2 shows most of

the registers necessary to control the serial interface.

ADAPTER LOGICAL NAME BASE USUAL CONTENTS

COM1 [0000:0400] 3F8

COM2 [0000:0402] 2F8

Third comm adapter [0000:0404]

Fourth comm adapter [0000:0406]

OFFSET COM1 Example REGISTER'S FUNCTION

0 3F8 Data

1 3F9 Interrupt Enable

2 3FA Interrupt Identification

3 3FB Data Format

4 3FC Serial Control

5 3FD Serial Status

6 3FE Input Status

Data Register (Register 0)

Bit 0 Data bit 0

Bit 1 Data bit 1

Bit 2 Data bit 2

Bit 3 Data bit 3

Bit 4 Data bit 4

Bit 5 Data bit 5

Bit 6 Data bit 6

Bit 7 Data bit 7

Interrupt Enable Register (Register 1)

Bit 0 Data Ready

Bit 1 Transmitter Empty

Bit 2 Serial Status Change

Bit 3 Input Status Change

Bits 4 - 7 Not Used

Interrupt Identification Register (Register 2)

Bit 0 0 Means Interrupt Pending

Bits 1 - 2 Interrupt ID;

--- 00 Serial Status Int.

|2|1| 01 Transmitter Status Int.

--- 10 Data Ready Int.

11 Input Status Int.

Bits 3 - 7 Not Used

FIGURE 11-2. The serial interface at a glance

(continued on the next page)

.pa

(continued from the last page)

Data Format Register (Register 3)

Bits 0 - 1 Word Length;

--- 00 5 bits

|1|0| 01 6 bits

--- 10 7 bits

11 8 bits

Bit 2 Stop bits:

0 1 bit

1 2 bits

Bit 3 Parity Enable:

1 Parity On

0 Parity Off

Bit 4 Parity:

1 Even Parity

0 Odd Parity

Bit 5 Stick Parity

Bit 6 Set Break

Bit 7 (selects) Baud Rate Divisor

Serial Control Register (Register 4)

Bit 0 Data Terminal Ready (pin 20)

Bit 1 Request To Send (pin 4)

Bit 2 OUT1 (user defined int. request)

Bit 3 Out2 (enable interrupts)

Bit 4 Loop Test

Bits 5 - 7 Not Used

Serial Status Register (Register 5)

Bit 0 Data Ready

Bit 1 Overrun Error

Bit 2 Parity Error

Bit 3 Froming Error

Bit 4 Received Break

Bit 5 Transmitter Holding Reg. Empty

Bit 6 Transmitter Shift Reg. Empty

Bit 7 Not Used

Input Status Register (Register 6)

Bit 0 Change On Pin 5

Bit 1 Change On Pin 20

Bit 2 Change On Pin 22

Bit 3 Change On Pin 8

Bit 4 Clear To Send (CTS)

Bit 5 Data Set Ready (DSR)

Bit 6 Ring Indicator (RI)

Bit 7 Data Carrier Detect (DCD)

-------- ----- ------ ----- ----- --------- ----- -----

FIGURE 11-2. The serial interface at a glance

If you compare Figure 11-2 with Figure 11-1, you will see how

much more complex than the parallel interface the serial

interface really is. There is a lot more to control with serial

I/O.

Base Addresses Serial Registers - Figure 11-2 shows, from top to

bottom, the locations in memory of the base addresses of each of

the four possible communications adapters. Of course, you could

add many more serial ports if you used unreserved interrupts, but

the four reserved interrupt addresses are in a place in memory

where they are not likely to be interfered with. DOS recognizes

two logical names, COM1 and COM2. Com1 is also called AUX. These

names are shown in the figure, along with their address vectors

and the addresses vectors and the addresses to which they are

typically initialized on an IBM PC. Square brackets indicate the

contents pointed to by an address. Thus, [0000:0400] points to

the word that holds the address 3F8 (hex).

Serial Interface Registers - The registers on the communications

adapter are in a series of addresses that follow the base address

consecutively. You can refer to each register relative to the

base address for the adapter. The second part of Figure 11-2

shows the offsets of each of the six communications registers

from the base address.

The Data Register - The data register is the simplest of the six

registers to understand. It holds a single byte either to send or

to receive. Whether you are sending or receiving depends on

whether you are writing or reading this register. If you use an

outp() to the data register, the data will be prepared and sent

to the receiving device. If you use an inp() from the data

register, the data in it will be made available to you.

If you are sending the byte, you need to know that the

receiving device has room to receive it. If you are receiving the

byte, you need to know that the sending device has indeed sent

something and that a byte is waiting to be read.

The Interrupt Enable Register - If you wish to install an

interrupt-driven serial interface, you must select the kind of

event that will signal the processor that an interrupt

needs service. If any combination of these bits are set, the

event or combination of events will create an interrupt. In

addition, there is a priority in which these interrupt events

will be served.

Interrupt Service Priorities - Any change in the input status will

receive the highest priority. In other words, the input lines

(wires) themselves, if they change state at the same time that

any other change takes place, will be served first.

If data is received (Data Ready), its service will come next.

In other words, if Data Ready occurs at the same time as

Transmitter Empty, then Data Ready will be reserved first.

If the transmiter holding register is empty (as indicated by

Transmitter Empty), it will be served next in priority after Data

Ready. It has the third level of priority.

Finally, a change in the modem status (Serial Status) will be

the last served by the interrupt mechanism. Serial status

indicates whether translation was successful. Any of a number of

errors or other important conditions can occur, and these are

monitored by the serial status register.

The Interrupt Identification Register - When you have enabled

interrupts by using the interrupt-enable register, the serial

control register, and the 8259 interrupt mask register (at 0x21),

you may wish to know when one of the interrupts occurs. You can

receive the current interrupt status from the interrupt

identification register. For example, you may wish to install an

interrupt service routine that is executed only if an interrupt

of a certain type is pending.

The Data Format Register - The data format register sets the data

bits, a parity bit, stop bits, and a baud rate. These govern the

way serial data will be interpreted. You have many options for

these settings.

You could set this register directly or use the DOS MODE

command before running your program. From an overall system

standpoint, you should, as much as possible, keep your software

from setting serial ports internally. From the standpoint of ease

of installation, however, you may wish to set the port for your

user, and then reset it when the user exits the software.

To set the number of data bits, you change bits 0 and 1 of the

data format register, as shown in Figure 11-2. Values of 5 and 6

bits are seldom (if ever) used.

To set the number of stop bits, you change bit 2 of the data

format register. Settings with two stop bits are seldom used.

Remember - there is always one start bit (always 0) and always at

least one stop bit in a complete serial transfer. The two bits,

one always on and one always off, are used to establish

synchronization with the clock.

You can have one or two stop bits. If you specify one stop

bit, eight data bits, and no parity, you will send a total of ten

bits per transfer cycle. There will be one start bit (always),

eight data bits, and the stop bit you requested. This tends to be

confusing, but the actual format is unimportant. The important

part is to make sure that the format used by the receiver matches

the format used by the transmitter.

Selecting Parity - To select parity, you change bit 3 of the data

format register. If on, this bit enables parity checking. This

means that right after the data a bit will be added that reflects

the sum of the bits in the data. If parity is enabled, an

automatic sum is generated each time a full eight bits is

received. The status of the parity bit received will be compared

with the sum of the data bits just generated. If the sum is odd

and the parity bit received says it should be even, a parity

error has occurred.

To select whether the expected parity will be odd or even, you

set bit 4 of the data format register. If you wish it to be even,

turn bit 4 on. If you expect parity to be odd, turn bit 4 off. No

matter how bit 4 is set, it will mean nothing if bit 3 is not on.

The stick parity bit (bit 5) adds to the confusion of it all by

reversing the meaning of the parity bit (bit 4). Make sure you

turn it off.

Break Detection - Bit 6 of the data format register is used to

send a break signal to a terminal. It does this by holding all

data to space voltages (-12 volts) long enough for the terminal

to detect a break in transmission.

The Baud Rate Divisor Latch - Last but not least, bit 7 of the

data format register changes the meaning of registers 0 and 1. It

selects the Baud Rate Divisor Latch. When bit 7 of register 3 is

on, the data register contains the low byte of the baud rate

divisor, and the interrupt enable register contains the high byte

of the baud rate divisor. If you intend to receive or transmit

data, make sure that bit 7 of the data format register (register

3) is off.

The Baud Rate Divisor Latch (see in registers 0 and 1 when bit

7 of register 3 is high) contains a number that, when multiplied

by 16 and divided into the magic clock frequency number

1,843,200, generates a baud rate. The baud rate divisor can be

determined and set by using Function 11-4, SET_BAUD.C.

The Serial Control Register - The serial control register sets the

state of two pins on the serial interface connector, enables

interrupts, and places the communications adapter into test mode.

If you wish to signal a sender to stop sending, you can do so by

lowering pins 4 and/or 20. Of course, the wire you use must be

connected to the sender's pin 5 or it will not do anything. Bit 2

of the serial control register is disabled on some boards and

functions identically to bit 3 on others. Bit 3 has a very

special function.

FUNCTION 11-4

-------- ----- ------ ----- ----- --------- ----- -------

SET_BAUD.C

-------- ----- ------ ----- ----- --------- ----- -------

/* SET_BAUD.C sets baud rate given the

** desired rate. It computes the divisor

** necessary. You should stick to conventional

** rates from 50 through 9600, exceeding at

** your own risk.

*/

union

byte;

struct

whole;

}bytes;

set_baud(baud_rate)

int baud rate;

;

struct ISRVEC isrvec;

unsigned char serisr(), readbuf();

unsigned buffchr();

.pa

Function 11-5

-------- ----- ------ ----- ----- --------- ----- -------

SERISR.ASM

-------- ----- ------ ----- ----- --------- ----- -------

; SERISR.ASM is a serial interrupt service routine.

_TEXT SEGMENT BYTE PUBLIC 'CODE'

_TEXT ENDS

CONST SEGMENT WORD PUBLIC 'CONST'

CONST ENDS

_BSS SEGMENT WORD PUBLIC 'BSS'

_BSS ENDS

_DATA SEGMENT WORD PUBLIC 'DATA'

_DATA ENDS

DGROUP GROUP CONST, _BSS, _DATA

ASSUME CS: _TEXT, DS: DGROUP, ES: DGROUP

TOTAL equ 64

TOP equ 63

BOTTOM equ 0

_DATA SEGMENT

buffer db TOTAL dup('@')

front dw BOTTOM

back dw BOTTOM

count dw BOTTOM

_DATA ENDS

_TEXT SEGMENT

PUBLIC_serisr

_serisr PROC FAR

push ax

push dx

push si

push ds

mov ax, DGROUP

mov ds, ax

mov dx, 3F8h ;base register address

in a1, dx ;input a character

mov si, front

mov buffer[si], a1

inc front

inc count

cmp front, TOP

jbe front_below

mov front, BOTTOM

front_below:

cmp count, TOTAL

jbe count_below

mov count, TOTAL

count_below:

mov al, 20h ; signal interrupt mask

out 20h, al ; register complete

pop ds

pop si

pop dx

pop ax

iret

_serisr ENDP

PUBLIC _buffchr

_buffchr PROC FAR

cli

push bp

mov bp, sp

push ds

mov ax, DGROUP

mov ds, ax

xor ax, ax

mov ax, count

pop ds

mov sp, bp

pop bp

sti

ret

_buffchar ENDP

Function 11-5 (continued)

-------- ----- ------ ----- ----- --------- ----- -------

SERISR.ASM

-------- ----- ------ ----- ----- --------- ----- -------

PUBLIC _readbuf

_readbuf PROC FAR

cli

push bp

mov bp, sp

push si

push ds

mov ax, DGROUP

mov ds, ax

xor ax, ax

cmp count, BOTTOM

jz back_below

mov si, back

mov al, buffer[si]

dec count

inc back

cmp back, TOP

jbe back_below

mov back, BOTTOM

back_below:

pop ds

pop si

mov sp, bp

pop bp

sti

ret

_readbuf ENDP

_TEXT ENDS

END

The ISRVEC Structure - If you study Functions 11-6 and 11-7, you

will see that they share the ISRVEC structure. The values placed

in this structure by Function 11-6 are used in Function 11-7 just

before you exit from your program back to DOS. In this way the

original interrupt vector is restored in a clean and responsible

way.

FUNCTION 11-6

-------- ----- ------ ----- ----- --------- ----- --------

INSTISR.C

-------- ----- ------ ----- ----- --------- ----- --------

/* INSTISR.C installs the serial interrupt service

** routine. Refer to 'serial interface at a glance'

** for register offsets used.

*/

#include <stdio.h>

#include <conio.h>

#include <dos.h>

struct ISRVEC

;

extern struct ISRVEC isrvec;

instisr(srvice, request, port)

unsigned (*resvice) ();

unsigned request;

unsigned long port;

setvect(vector, service)

int vector;

unsigned (*service) ();

The buffchr() Function - The word at offset 68 contains a nonzero

value when there is data in the buffer. You can check it while

your program is running to see if any goodies have been gathered

from the sending device. To read the count variable, because it

is not public, you must use the buffchr() function contained in

Function 11-5. If this function returns a nonzero value, it

indicates that there is at least one character in the rotating

buffer. This count is never allowed to exceed the total number of

characters in the buffer.

FUNCTION 11 - 7

-------- ----- ------ ----- ----- --------- ----- --------

UNINSTAL.C

-------- ----- ------ ----- ----- --------- ----- --------

/* UNINSTAL.C uninstalls the serial interrupt service

** routine.

*/

#include <stdio.h>

#include <dos.h>

#include <conio.h>

#define SERPORT1 0x00000400L

#define SERPORT2 0x00000402L

#define SERVEC1 12

#define SERVEC2 11

struct ISRVEC

;

extern struct ISRVEC isrvec;

uninstal(port)

unsigned long port;

unsetvec()

The readbuf() Function - To read a character from the back of the

buffer, you must use the readbuf() function, which is given in

Function 11-5. The interrupt service buffer is intentionally kept

local so that it does not interact with external data. This means

that you must read the buffer with a function that knows where

the buffer is. Interrupts must be turned off before the buffer is

read and turned on again before the function returns to its

caller.

Function 11-5 will handle interrupts from each occurrence of a

full data latch, intercepting each byte as it arrives from the

sender, until the buffer is full. The current character is placed

at the front of the buffer, and the back of the buffer is read by

your program. The delay between the receipt of characters from

the sender and their retrieval by your program can be decreased

by decreasing the size of the receiver buffer. The buffer shown

is 64 bytes long. You can change the number of bytes, but be sure

to reflect the change throughout the routine. If you make the

buffer 20 bytes long, the offsets 64, 66, and 68 must be made 20,

22, and 24, respectively. The buffer never overflows, but if you

do not check it before it goes through one complete cycle, you

will lose one cycle`s worth of data.

Overrun Errors - If a byte arrives in the latch before the current

byte has been read from the latch, the interrupt-service routine

will receive garbled data. If you find that garbled data is being

received, lower the sender`s and receiver`s baud rates. The

interrupt-service routine is generally able to accommodate 9600

baud easily, however.

Making Interrupt-Service Routines Work -Making interrupt-service

routines work can be a difficult undertaking. The primary frus

tration is that not only must you debug your program, but you

must be aware that the interrupt-service routine is working

constantly while your software is running. Interrupts can occur

at any time. Unless you carefully coordinate the interrupt

service so that it does not interfere with the processes that are

being interrupted, you will find that mysterious things happen.

Function 11-5 has been carefully designed to use variables that

are never public. If public variables or calls are used,

interrupt-service routines can become interactive in a very

negative way.

Serial Interrupt Service Works Well -Be assured that Functions

11-5 through 11-7 will work well if you take the time to compile,

assemble, and link them exactly as they are. The serial interrupt

service they install and maintain will be the primary source of

input from your serial devices. These functions represent one

easy technique to make serial input fast and easy for every

application. The advantage of serial interrupts is that they can

keep up with the fastest senders and give you information only

when you want it. 9600 baud is IBM`s recommended limit, but it

can be exceeded at your own risk. Since most C compilers do not

support interrupt-service handling. Functions 11-5 through 11-7

should prove to be very valuable to you in all applications, not

just for graphics.

Digitizer Interfacing

It is difficult to speak in general terms about digitizer

interfacing. Digitizers are perhaps the least standardized of all

computer devices. The tradition in digitizer design is very

liberal. For this reason, as with other devices, a specific

digitizer is used here as an example. This does not constitute an

endorsement of the product, but the digitizer chosen is very

popular.

The Kurta Series One digitizer uses a serial interface to

communicate with the computer. If you use it as it is shipped

from the factory, its cable will plug into your COM1 port, and so

it will be easy to interface. If you wish to employ hardwire

handshaking, follow the user manual to learn how to connect pin 4

(the orange wire inside the connector shell) to the orange wire

coming from the digitizer. This connection is not made at the

factory, but it will enable your software to stop and start the

digitizer using bit 1 of register 4. (See Figure 11-2 for

details.)

Setting the Digitizer`s Switches - To work with serial interrupt

input from the digitizer, you should see the switches on the back

of the digitizer for 1200 baud. There are two banks of switches.

One has four positions, and the other has eight.

On the four-position switch, set all the switches to off. On

the eight-position switch, set positions 2, 3, and 8 to on and

all the others off. These switches govern option settings and

baud rate. Read about them in the digitizer manual.

Setting the Baud Rate - In the early days of the IBM PC,

applications were designed to set the baud rate within most

programs, ignoring the needs of the operating system. These days,

although it is less convenient, the user is encouraged to

establish a system-wide serial interface initialization. If

software changes this system-wide setting, it should restore it

on exit. Function 11-8, TEST_DIG.C, will configure the port for

1200 baud, no parity, 8 data bits, 1 stop bit, and no device

timeouts.

Testing the Digitizer - Using Function 11-5 as shown in Function

11-8, you can see a data stream from the digitizer. Assemble,

compile, and link Functions 11-5 through 11-8. Raw data from the

digitizer (or any other properly configured serial transmitter)

should appear rapidly on the display. As you move the digitizer

stylus, the characters will change. If they jump around wildly

from value to value, you are getting overrun errors, and you

should lower the baud rates of the receiver and sender.

FUNCTION 11-8

-------- ----- ------ ----- ----- --------- ----- --------

TEST_DIG.C

-------- ----- ------ ----- ----- --------- ----- --------

/* TEST_DIG.C tests the digitizer using

** serial interrupt service.

*

#include <stdio.h>

#include <conio.h>

#include <string.h>

#define SERPORT1 0x00000400L /* COM1 interrupt address */

#define SERPORT2 0x00000402L /* COM2 interrupt address */

#define SERVEC1 12 /* COM1 interrupt request (IRQ4) */

#define SERVEC2 11 /* COM2 interrupt request (IRQ3) */

struct ISRVEC

;

struct ISRVEC isrvec;

unsigned char serisr(), readbuf();

unsigned buffchr();

main()

if (thisbuff[5] >= 0x80)

}

}

getch(); /* dispose of pending character */

uninstal(SERPORT1);

/* restore port settings */

outp(*outbase + 3, bits);

outp(*outbase + 0, baudlo);

outp(*outbase + 1, baudhi);

/* turn off divisor latch select bit */

outp(*outbase + 3, inp(*outbase + 3) & 0x7F);

}

When you make a legal exit by pressing a key (other than a

control or shift key) on the keyboard, the original baud rate and

other settings of the serial port will be restored. To make sense

of the binary numbers, read your digitizer`s documentation and

study GRAPHIQ.C in Appendix A.

.pa

Polled Serial Interfacing

If you do not need the elaborate services of an interrupt-driven

serial input routine, you can easily implement polled serial

input. Polled serial input is an input method that waits for a

byte to enter the receiver latch before receiving it. Using

polled serial input, you ask for each character as it becomes

ready.

For most purposes, polled serial I/O is perfectly adequate, as

long as you keep the sender`s baud rate low enough so as not to

overrun the byte in the latch before it is read. You can always

test the overrun error bit (bit 1) in the status register

(register base+5). If it is on, data is being sent too fast for

your software to receive it. Your software can report overrun

errors to the user and explain how to lower the baud rate.

Function 11-9, COM_STR.ASM, gets as many characters as you

specify from the serial port and then lets the port continue

receiving data until the next poll. This function is particularly

good for use with digitizers because it always returns data that

is currently being sent. When using a digitizer or mouse, you do

not want data that has been buffered, because you do not want a

time delay between the coordinates you receive and the actual

position of the digitizer stylus.

Function 11-9 is written in assembler because it must run as

fast as possible. If it runs too slowly, it will be unable to

receive a character from the data latch before the next one

arrives. His overrun condition will result in garbled data. You

use Function 11-9, after assembling it, just as though it were a

function written in C.

FUNCTION 11-9

-------- ----- ------ ----- ----- --------- ----- --------

COM_STR.ASM

-------- ----- ------ ----- ----- --------- ----- --------

; COM_STR.ASM fetches a desired number of bytes

; (0 to 32767) from the serial input device.

; Usage from C:

;

; char buffer[0];

; int count;

;

; com_str(buffer, count);

;

; RETURNs:

; string of count characters in buffer

; return value 0

COM_STR_TEXT SEGMENT BYTE PUBLIC `CODE`

COM_STR_TEXT ENDS

CONST SEGMENT WORD PUBLIC `CONST`

CONST ENDS

_BSS SEGMENT WORD PUBLIC `BSS`

_BSS ENDS

_DATA SEGMENT WORD PUBLIC `DATA`

_DATA ENDS

DGROUP GROUP CONST, _BSS, _DATA

ASSUME CS: COM_STR_TEXT, DS: DGRO, SS: DGROUP, ES: DGROUP

PUBLIC =com_str

COM_STR_TEXT SEGMENT

PUBLIC _com_str

_com_str PROC FAR

push bp

mov bp, sp

; get pointer from stack

mov bx, [bp + 6]

; get count from stack

mov cx, Word Ptr [bp + 8]

cmp cx, 0

je exit

and cx, 7FFFh ; strip off high bit in

; case negative int was passed

not_ready:

; test for character

; in latch and no errors

mov dx, 3F8h ; Register 0 (base)

add dx, 5 ; Register 5

in al, dx ; Serial Status Register

test al, l ; Is only Data Ready set?

jz not_ready

; receive the character

mov dx, 3F3h ; Register 0 (base)

add dx, 3 ; Register 3

FUNCTION 11-9 (continued)

-------- ----- ------ ----- ----- --------- ----- --------

COM_STR.ASM

-------- ----- ------ ----- ----- --------- ----- --------

in al, dx ; Data Format Register

and dx, al ; not selected

mov dx, 3F8h ; Register 0 (base)

in al, dx ; input the type

; store byte in public string

mov Byte Ptr [bx], al

inc bx ; point to next character

; supply ASCIIZ string

; terminator (null byte)

mov Byte Ptr [bx], 0

dec cx

cmp cx, 0

jbe exit ; exit when cx reached

loop not_ready

exit:

mov ax, 0 ; always returns 0

mov sp, bp

pop bp

ret

_com_str ENDP

COM_STR_TEXT ENDS

END

Mouse Interfacing

The mouse, which is perhaps even more ubiquitous than the

digitizer, can also be used as a locator. The advantage in using

a mouse is that it is inexpensive, convenient, and supported by

most modern software. The only real disadvantage in using a mouse

is that you can work only with relative coordinates.

Relative coordinates are derived from the last known position of

the locator, rather than from a constant reference point. In

other words, the mouse knows only its last position and the

distance it has moved from it, not its absolute location on a

grid.

Unless you are tracing drawings, you will not need a locator

that has absolute position-reporting capabilities. To select from

menus, you need only to sense the direction of each successive

movement of the locator - whether up, down, left, or right.

The Mouse - The mouse used for the purposes of this description is

made by Mouse Systems. As with the digitizer, this does not

constitue an endorsement of this particular mouse, but it does

recognize that this mouse is very popular and thus suitable for a

general description of mouse interfacing techniques.

Function 11-9 will be used to receive a stream of characters

from the port to which your mouse is connected. For purposes of

demonstration, this will be COM1, but you can modify the source

code as you choose. You are strongly encouraged to try the mouse

with the serial interrupt-service routines of Functions 11-5

through 11-7 as well.

Receiving a Single Character - To receive one character at a time

quickly from a serial port, you should use a function like

Function 11-10, GET_SER.ASM. Written in assembler and callable

from C, this function waits for a character to appear in the data

latch and then reads the character. You could use Microsoft C`s

inp() function to do the same thing, but it would be slower,

especially using the Medium memory model, as is done throughout

this book. If you are receiving characters one at a time this

way, the baud rates of receiver and sender may need to be kept

very low in order not to overrun the data latch. If you use the

serial interrupt-service routine of Function 11-5 through 11-7,

you will not encounter this problem for baud rates of up to 9600.

FUNCTION 11-10

-------- ----- ------ ----- ----- --------- ----- --------

GET_SER.ASM

-------- ----- ------ ----- ----- --------- ----- --------

; GET_SER.ASM gets a single character from COM1.

; Declaration:

; char get_ser();

; char character;

; Usage:

; character = get_ser();

FUNCTION 11-10 (continued)

-------- ----- ------ ----- ----- --------- ----- --------

GET_SER.ASM

-------- ----- ------ ----- ----- --------- ----- --------

GET_SER_TEXT SEGMENT BYTE PUBLIC `CODE`

GET_SER_TEXT ENDS

CONST SEGMENT WORD PUBLIC `CONST`

CONST ENDS

_BSS SEGMENT WORD PUBLIC `BSS`

_BSS ENDS

_DATA SEGMENT WORD PUBLIC `DATA`

_DATA ENDS

DGROUP GROUP CONST, _BSS, _DATA

ASSUME CS: GET_SER_TEXT, DS: DGROUP, SS: DGROUP, ES: DGROUP

PUBLIC _get_ser

GET_SER_TEXT SEGMENT

PUBLIC _get_ser

_get_ser PROC FAR

push bp

mov bp,sp

not_ready:

; test for character

; in latch and no errors

mov dx, 3F8h ; Register 0 (base)

add dx, 5 ; Register 5

in al, dx ; Serial Status Register

text al, 1 ; Is only Data Ready set?

jz not_ready

; receive the character

mov dx, 3F8h ; Register 0 (base)

add dx, 3 ; Register 3

in al, dx ; Data Format Register

and a 7Fh ; make sure divisor latch

out dx, al ; not selected

mov dx, 3F8h ; Register 0 (base)

in al, dx ; input the type

mov sp, bp

pop bp

ret

_get_ser ENDP

GET_SER_TEXT ENDS

END

Getting Mouse Data Function 11-11, GET_MOUS.C, gets bytes sent

from the mouse over the serial interface and displays them using

standard output to the display. This function also shows how to

initialize the serial port and restore it on exit. The mouse

operates at 1200 baud with 8 data bits, 1 start bit, and 1 stop

bit. It uses no parity checking.

Compile Function 11-11 and link it with Function 11-10 to see

the mouse in action. As you move the mouse on its little pad, you

will see characters on the display that reflect its movement. As

you press buttons on the mouse, characters will appear that

reflect the button pressed.

The Limitations of Polled Mouse Interface - Function 11-11 is only

an example that gets some data from the mouse. It shows only one

way to interface the mouse. You can also use an interrupt-service

routine that you write yourself, like Function 11-5, or you can

use software tools provided by the mouse manufacturer. Since such

tools solve most of your low-level problems, they are highly

recommended.

Light Pen Interfacing

A light pen interface is perphaps the easiest interface of all.

The EGA supports light pens from a wide range of manufacturers,

and so no specific piece of hardware need be discussed. The EGA

BIOS service 4 of interrupt 10 (hex) is adequate to report both

the row and column number of the light pen in character positions

and the location of the pen in pixels.

Reading the Light Pen Position and Switch Function 11-12,

READ_PEN.C, reads the light pen position and indicates whether

the pen switch is pressed or not. The code is very direct and

will work no matter what mode the EGA is in.

FUNCTION 11-11

-------- ----- ------ ----- ----- --------- ----- --------

GET_MOUS.C

-------- ----- ------ ----- ----- --------- ----- --------

/* GET_MOUS.C gets an input stream of characters from

** the scurrying mouse. To make sense of the characters

** see your mouse documentation. Try modifying this

** function to report the x, y location and buttons.

*/

#include <stdio.h>

#include <conio.h>

#define SERPORT1 0x00000400L

#define SERPORT2 0x00000402L

main()

getch(); /* waste character from keyboard */

/* restore port settings */

outp(*base + 3, bits);

outp(*base + 0, baudlo);

outp(*base + 1, baudhi);

/* turn off divisor latch select bit */

outp(*base + 3, inp(*base + 3) & 0x7F);

}

FUNCTION 11-12

-------- ----- ------ ----- ----- --------- ----- --------

READ_PEN.C

-------- ----- ------ ----- ----- --------- ----- --------

/* READ_PEN.C reads the light pen position

** and switch status. Return value is 1

** if switch has been triggered.

*/

#include <conio.h>

#include <dos.h>

read_pen(row, col, x, y)

int *row, *col, *x, *y;

return(regs.h.ah)

}

Serial Output Interfacing

Receiving information from the outside world is usually much more

difficult than sending it. That is why so much attention has been

given to receiving data from the parallel and serial ports.

You can send data out a serial port merely by outputting bytes

to the serial port`s base register. You will face two major

problems if you try this. First, you must make sure that there

is nothing in the data latch that should be read.Second, you must

know if the receiving device has room for the data you are about

to send.

Output Verification and Handshaking - If you refer to Figure 11-2,

you will see the serial status register (register 5). Bit 0 of

this register, data ready, indicates that a byte is available in

the latch and can be read. If this bit is high and you are

interested in the information in the latch, you must receive it

before attempting to transmit a byte. Likewise, if bit 5 of

register r is 0, it means there is a byte in the transmitter

holding register that has not yet been sent. If you care about

the contents of this register, you must wait for this bit to be

set to high before trying to send a character.

The device to which you are sending will usually control pin 5

of the interface, setting it to high if you are clear to send

data and low if you are not clear to send. You must monitor pin 5

to see if the receiving device will permit you to send a byte.

You can do this by monitoring bit 4 of the input status register

(register 6). This register keeps track of the real-time of the

handshake wires connected to the interface pins.

Sending Characters Interactively Function 11-13, SEND_SER.ASM,

is a function, written in assembler and callable from C, that

sends a byte, but will do so only after it has received any

pending characters from the data latch. It is useful for communi

cations programs where you whish to send and receive characters

interactively. The function has no way to stop the sender from

sending more characters, however, and should be used only in

situations where you know that the number of characters to be

received will be limited. If you want full control, you must

implement a handshake and hold wire 20 to the destination low

until you can accept more characters. Function 11-13 must be

called in rapid succession in a tight loop if you are to avoid

overrun errors. If the loop does not run fast enough and will

work only at an extremely low baud rate, implement more of the

loop directly in assembler.

FUNCTION 11-13

-------- ----- ------ ----- ----- --------- ----- --------

SEND_SER.ASM

-------- ----- ------ ----- ----- --------- ----- --------

; SEND_SER.ASM sends and receives characters interactively.

; Declaration:

; char send_char();

; char character;

; Usage;

; rec_char = send_ser(character);

;

; Returns:

; if not zero, return is received character

; Note:

; requires pin 4 held high by destination to send

; no control. of pins 5 or 20

SEND_SER_TEXT SEGMENT BYTE PUBLIC `CODE`

SEND_SER_TEXT ENDS

CONST SEGMENT WORD PUBLIC `CONST`

CONST ENDS

_BSS SEGMENT WORD PUBLIC `BSS`

_BSS ENDS

_DATA SEGMENT WORD PUBLIC `DATA`

FUNCTION 11-13 (continued)

-------- ----- ------ ----- ----- --------- ----- --------

SEND_SER.ASM

-------- ----- ------ ----- ----- --------- ----- --------

_DATA ENDS

DGROUP GROUP CONST, _BSS, _DATA

ASSUME CS: SEND_SER_TEXT, DS: DGROUP, SS: DGROUP, ES: DGROUP

PUBLIC _send_ser

SEND_SER_TEXT SEGMENT

PUBLIC _send_ser

_send_ser PROC FAR

push bp

mov bp,sp

xor ax, ax ; clear AX

; text for character

; in latch and no errors

mov dx, 3F8h ; Register 0 (base)

add dx, 5 ; Register 5

in bh, dx ; Serial Status Register

test bh, 1 ; Is only Data Ready set?

jz no_receive

; receive the character

mov dx, 3F8h ; Register 0 (base)

add dx, 3 ; Register 3

in a1, dx ; Data Format Register

and a1, 7Fh ; make sure divisor latch

out dx, a1 ; not selected

mov dx, 3F8h ; Register 0 (base)

in a1, dx ; input the byte

no_receive:

mov dx, 3F8h ; Register 0 (base)

add dx, 6 ; Input Status Register

in bh, dx ; examine register 6

test bh, 10h ; is bit 4 (pin 5) on?

jz no_receive ; if not, wait until it is

not_empty:

mov dx, 3F8h ; Register0 (base)

add dx, 5 ; Serial Status Register

in bh, dx ; examine register 5

test bh, 90h ; are bits 5 and 6 on?

jz not_empty ; if not, wait until they are

; get character from stack

mov b1, [bp + 6]

mov dx, 3F8h ; Register 0 (base)

out dx, b1 ; send the character

; if char was received in a1

; it will be returned on exit

mov sp,bp

pop bp

ret

_send_ser ENDP

SEND_SER_TEXT ENDS

END

If at First You Don`t Succeed Serial interfacing can be

extremely frustrating. Keep in mind that there are many complex

variables involved and that the functions in this book, although

they work in isolated test situations, may not work if any of a

million combinations of factors are changed. It is impossible to

give examples of all the possible combinations here. On the other

hand, the functions are all sound in principle and throughly

illustrate the options available using serial interface

programming. If you carefully assemble, compile, and link

Functions 11-5 through 11-7 and use serial interrupt service in

your software, you can have done with serial input once and for

all.



Politica de confidentialitate | Termeni si conditii de utilizare



DISTRIBUIE DOCUMENTUL

Comentarii


Vizualizari: 1411
Importanta: rank

Comenteaza documentul:

Te rugam sa te autentifici sau sa iti faci cont pentru a putea comenta

Creaza cont nou

Termeni si conditii de utilizare | Contact
© SCRIGROUP 2024 . All rights reserved