home *** CD-ROM | disk | FTP | other *** search
- 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.