home
***
CD-ROM
|
disk
|
FTP
|
other
***
search
/
Power Programming
/
powerprogramming1994.iso
/
progtool
/
modem
/
async1.arc
/
ASYNC.DOC
< prev
Wrap
Text File
|
1985-09-13
|
14KB
|
276 lines
Async.Asm
Operation Notes
by Jerry D. Stuckle
Userid: 72205,234
Async.Asm is an example assembler program to drive the Asynchronous Adapter
in the IBM-PC. It accepts the same parameters as the ROM BIOS Interrupt 14
code, with the exception that only one port (COM1:) is available. The code
can be easily modified to handle two communications ports, but for 1 only
wrote the code for one port to keep things simple.
This routine provides a buffered byte-stream interface to the
communications port. The following parameters can be used.
AH=0 Open communications port.
AL has initialization parameters
7 6 5 4 3 2 1 0
----Baud Rate---- --Parity-- -Stop- -Word Len-
000 - 110 X0 - None 0 - 1 00 - 5 bits
001 - 150 01 - Odd 1 - 2 01 - 6 bits
010 - 300 11 - Even 10 - 7 bits
011 - 600 11 - 8 bits
100 - 1200
101 - 2400
110 - 4800
111 - 9600
On return, conditions set as in call to comm status (AH=3)
AH=1 Send the character in AL to the port. (AL is preserved). On exit,
Bit 7 of AH is set if the buffer is full. If Bit 7 of AH is not set, the
remainder of AH is set as in status request, reflecting the current status of
the line.
AH=2 Receive a character in AL from the port before returning to the
caller. On Exit, if AH is 0FF, the buffer was found to be empty and AL is
indeterminate. Otherwise, AH has the current line status as set by the status
routine, except the only bits left on are the error bits (7,4,3,2,1). If AH
has bit 7 on (time out) the remaining bits are unpredictable. Thus, AH is
non-zero only if errors have occurred.
AH=3 Return the port status in AX.
AH contains the line status
Bit 7 = Time out
Bit 6 = Trans buffer empty
Bit 5 = Trans buffer not full
Bit 4 = Break detect
Bit 3 = Framing error
Bit 2 = Parity error
Bit 1 = Receive buffer overrun
Bit 0 = Data ready in receive buffer
AH=FF = Invalid parameter in request.
AL contains the modem status
Bit 7 = Received line signal detect
Bit 6 = Ring indicator
Bit 5 = Data set ready
Bit 4 = Clear to send
Bit 3 = Delta receive line signal detect
Bit 2 = Trailing edge ring indicator
Bit 1 = Delta data set ready
Bit 0 = Delta clear to send
AH=4 Close communications port. Resets the communications line and flushes
the buffers.
For all values of AH. DX = parameter indicating which communications port
to use (0 allowed).
Other than AX, all other registers returned unchanged for all calls.
Note: The following changes have been made from the BIOS interrupt 14
driver:
1: This routine is completely interrupt driven, with transmit and receive
buffers so the program does not have to wait for data.
2: Function call 0 (Initialize) will immediately return control to the
program. It is the programmer's responsibility to ensure the port is actually
ready before sending data, although data can be sent. This allows the program
to open the port with no carrier received.
3: AH=1 will return with Bit 7 of AH set only if the transmit buffer is
full. Otherwise, the character will be placed in the buffer and transmitted
when the port catches up with the buffer.
4: AH=4 is a new call. It will disable the interrupts and flush the
transmit and receive buffers. It should be called before program termination
to disable the port.
5: On all calls, the following bits in AH have changed:
Bit 6 is now Transmit buffer empty.
Bit 5 is now Transmit buffer not full.
Bit 1 is now Receive buffer overrun.
Bit 0 is now Receive buffer not empty.
These bits correspond to equivalent bits in the original code, i.e. Bit 0
was Data ready, and now indicates something in the receive buffer (data
ready).
The receive buffer overrun bit should not occur, if the receive buffer is
large enough and called frequently enough. If it does occur, the buffer data
is still good, but data will be missing and integrity compromised. The data
will be lost starting X bytes past the current buffer, where X is the buffer
size.
The program was written in a modular form, with mainline code performing
parameter verification checks and routine selection. All work is done in
procedures. Some simple macros are included for commonly used functions, and
the transmit and receive buffers are defined using a structure.
Also, I like to use both upper and lower case characters for coding in
assembler. All registers are in upper case only (AX,BX, etc.) while
everything else is capitalized (Functble, Cinit, etc.). I feel this makes the
code more readable, but it is up to the programmer writing the code what
format he/she uses. The only time it will make a difference is in comparing
parameters in a macro call (I.E. using the Ifidn <A>,<a>). This function is
case sensitive, and will fail in the example above.
Since we are working with mostly bytes and port address, this routine uses
.Radix 16. It could have also been written with the default of .Radix 10, but
then many values would have had to have an 'h' (hexadecimal) notation
following. As it stands, the only values which need the 'h' appended are
those ending in a 'b' or 'd', which would otherwise signify binary or decimal
representation, respectively.
There are two main procedures in the code. Int14 is the interrupt 14
handler, and is the user interface to the program. It replaces the existing
Int14 handler in BIOS and DOS, and for the most part contains the same
functions as the original handler. (See Async.Doc for specific information).
The other main procedure is Int0C, which is the machine's interface. It
uses the machine's hardware interrupt 0C from the communications port to drive
it. When status changes (i.e. character received, modem dropped ready, etc.)
the Int0C code is driven to handle the resultant hardware interrupt. This
allows the program to update the buffers and/or status indicators when they
occur, and relieves the program of the need to check except when it is ready
to receive the data.
The macros used in the program are defined at the beginning. These macros
are used in place of coding common code several times in the program, saving
time and the possibility of "finger checks". They also make reading the code
and debugging easier. The macros will expand in the listing of the code, so
you can see exactly what is generated for later debugging.
Liberal use of Subttl and Page pseudo-ops have been used to make output
formatting logical and easier to read. I have found the time saved trying to
find an area of code is much worth the few pages of extra paper used in a
large program. Also, it starts a procedure at the top of the page instead of
in the middle, making it easier to find later.
Unless absolutely necessary, all jumps should be in a forward direction. If
a jump must be made backwards (occasionally necessary for looping), it should
be as short a distance as possible. This will minimize the possibility of
loops which cannot be exited, and the resultant reboot which will be
necessary.
One other note: It is possible to program in a structured fashion in MASM,
if procedures are used effectively. The use of a procedure does not mean it
is called from multiple places in the program. Rather, it should be a logical
and complete unit or work - that is, a specific function should be desired
upon entry, and that function completed at the end. This modularity can allow
some common routines to be used in other programs, and definitely makes the
code easier to write and debug.
Operation of the code follows:
Upon entry, the program does a JMP to the initialization code at the end.
This code is placed at the end as it is used once (when invoked from DOS) and
not needed after that. If placed at the end, its storage can be reused at a
later time by other programs.
At label START, we first initialize the buffers pointers. This is one
fault with using structures - you can't set an offset from the start of the
program. Using the pseudo-op '$' (instruction counter) just gives you the
offset from the beginning of the structure. So we have to initialize the
pointers. After initializing the pointers, we set the interrupt 0C and 14
vectors and terminate. However, the Int 27 (used for DOS 1.x compatibility)
allows the storage to remain allocated, and the program resident in memory.
Once the program is installed, it is accessed through software through
interrupt 14. The hardware transfers control to procedure Int14, which
verifies the parameters and calls the correct handling routine. When control
is returned to this routine, it restores the registers saved on entry and
returns control to the caller through the Iret (Interrupt return) statement.
When control is passed to Int14, it will select a routine to call based on
the value in AH (request code). It moves the value to BX, multiplies by 2 and
uses this as an offset into a function call table. The use of a table like
this eliminates the need for checking every possible value of for the request,
and returns control to one common place in the code. It also makes it easier
to add routines. All you have to do is add your new routine and a pointer to
it in the table. The assembler will take care of everything else.
AH = 0. Copen is called which must be made before any other call. It will
initialize the system and asynch interrupt regs, flush the buffers of any
residual data, and set the initial status. Note that the system interrupt
register (port 21) must be enabled BEFORE the asynch board, or you will get a
lockout condition where you never get the interrupt processed. (This one cost
me four days of debugging!).
AH = 1. Csend will take the character in AL and place it in the transmit
buffer. If the buffer is full, it returns with the timeout bit set so the
program can resend at a later time. If the buffer is not full, it checks to
see if transmit interrupts are enabled. If they are, it returns with the
status in AH. If not, it enables the transmit interrupt so the interrupt 0C
routine can send the character.
AH = 2. Crcve will return a character from the buffer in AL and the status
in AH. If a character is not available, 0FF (-1) will be returned in AH so
the program can check later.
AH = 3. Cstat returns the current status in AH and modem status in AL.
AH = 4. Cclos will disable interrupts from the asynch port and system and
flush the buffers. It must be the last function called when the program is
through with the port. Also note that the asynch port must be disabled BEFORE
the system, or an incoming interrupt may cause problems next time the
interrupts are enabled.
The hardware interrupt processing procedure, Int0C, gets control whenever
the communications port interrupts the system. It runs asynchronously (pardon
the pun) with the rest of the system, and is completely hardware controlled.
The main routine resets the hardware controller at port 20 by putting an x'20'
to it, determines what kind of interrupt is pending, and calls the appropriate
routine. This loop will continue until all outstanding interrupts are
handled. This code also uses a branch table and is the only place we have a
backward jump in the code. The correct routine is called, and the loop
continues until the interrupt id register contains 01 (no interrupts pending).
Procedure Modemst handles interrupt code 00 (modem status change). This
reads the modem status port and sets it into Mstatus for use by the program.
Procedure Xmithrg handles interrupt code 02 (transmitter holding register
empty). If a byte is available in the transmit buffer, it is placed in the
transmit register. If not, the transmit holding reg empty interrupt is
disabled.
Procedure Rdatint handles interrupt code 04 (receive data available). This
is probably the most important interrupt to handles, as if this is not handled
quickly enough, another character will overlay the one currently in the
register and data will be lost. If the receive buffer is full, the character
is discarded and the overrun bit set in Lstatus.
Procedure Rcvrlst handles interrupt code 06 (receive line status). All it
has to do is read the line status register and set it into Lstatus.
This is a quick overview of the major blocks of the program. This is a
simple program with many changes which can be made. For example, the routine
could have XON/XOFF functions built in, the modem and line status could be
enhanced, and other changes made. However, this is a good base to start for
someone who wants to play with the communications port interrupts. Any simple
routine which will pass characters from the keyboard to the Int 14 handler,
and from the Int 14 handler to the display screen will work nicely to test
this program. This can be done in 50 lines or so of assembler code, and works
nicely as a test for the program.
===============================================================================
Changes in this upload:
1. The extra CLI at the beginning of the COPEN routine has been removed.
2. Extra code in the CSEND routine has been removed.
3. The line status code has been reworked to operate properly (and more
efficiently).
4. The only operational change was to set the high order bit (80h) if a
character was received with a parity error. As the parity error cannot
occur if running with 8 data bits, this will have no effect on binary
transfers of data.