In-depth introduction to serial programming of DOS using VC ++

Source: Internet
Author: User
On the DOS platform, you can use the following methods to operate the serial port: BIOS call, hardware interruption through the serial port, or round-robin of the serial port hardware, this chapter will introduce and give examples of the above three methods.

1. BIOS interruption

In the DOS Operating System, ibm pc and its compatible devices provide a flexible serial port I/O access method, that is, the ROM BIOS serial communication routine is called through INT 14H. When AH is set to a different value, different functions are generated:

AH 0 initialization Port
AH 1 write characters to the serial port
AH 2 read characters from the serial port
AH 3 fetch communication port status

When initializing the port (when AH = 0), you need to assign a byte of initialization parameter to the AL register. Meaning 1;


Figure 1 AL register settings when INT 14H is called

When writing characters to the serial port (when AH = 1), the characters in the AL register are the characters to be written;

When writing characters to the serial port (when AH = 2), the characters in the AL register are the characters to be read.

Take a look at the following routine:

# Include <stdio. h>
# Include <dos. h>
# Include <bios. h>
# Define STR "author: sbh"
Union REGS inregs, outregs;

Main ()
{
// Set serial port Parameters
Init_rs232 ();
// Example of serial port writing
Write_rs232 (STR, strlen (STR ));
// Example of reading the serial port
Read_rs232 ();

Return (0 );
}

Init_rs232 ()
{
Do {
Inregs. H. Ah = 0; // Ah = 0 indicates the initialization port.
Inregs. H. Al = 0xe7;
Inregs. X. dx = 0; // COM1
Int86 (0x14, & inregs, & outregs );
} While (outregs. H. Ah> = 0x80 );

Return (0 );
}

Write_rs232 (char * string, int Len)
{
Int I;
Do {
Inregs. H. Ah = 1; // send the character of the Al register
Inregs. H. Al = * string;
Inregs. X. dx = 0;
Int86 (0x14, & inregs, & outregs );
} While (outregs. H. Al> = 0x80 );

For (I = 1; I <Len; I ++)
{
Inregs. H. Ah = 1;
Inregs. H. Al = * (string + I );
Inregs. X. dx = 0;
Int86 (0x14, & inregs, & outregs );
}
}

Read_rs232 ()
{
Do {
Inregs. H. Ah = 2; // read characters in the Al register
Inregs. X. dx = 0;
Int86 (0x14, & inregs, & outregs );
} While (outregs. H. Al! = 3 | outregs. H. Ah> = 0x80 );

Return (0 );
}

The prototype of the int86 function used is:

Int _ Cdecl int86 (int intno, union REGS * inregs, union REGS * outregs );

The int86 () function can call the BIOS function. Currently, programmers have little access to this function, and 80% of programmers have never even seen this function. In fact, in the era of DOS, int86 () is almost one of the most common and core functions. It can be said that in that era, no int86 () means no programming. In combination with int86, a consortium such as REGS is defined:

Union REGS {
Struct WORDREGS x;
Struct BYTEREGS h;
};

The WORDREGS is defined:

Struct WORDREGS {
Unsigned int ax, bx, cx, dx, si, di,
Cflag/* Carry flag */,
Flags/* sign register */;
};

BYTEREGS is defined:

Struct BYTEREGS {
Unsigned char al, ah, bl, bh, cl, ch, dl, dh;
};

It turns out that WORDREGS and BYTEREGS are 16-bit registers in the 8086 processor! Therefore, when the CPU reaches 286 and 386, installing DOS is also based on the CPU usage mode!

Another function is similar to int86:

Int _ Cdecl int86x (int intno, union REGS inregs, union REGS outregs, struct SREGS segregs );

The SREGS is a segment register struct, defined:

Struct SREGS
{
Unsigned int es;
Unsigned int cs;
Unsigned int ss;
Unsigned int ds;
};

Both int86 and int86x functions execute a 8086 Soft Interrupt specified by the intno parameter. Before the Soft Interrupt is executed, both functions place the content in inregs into each register (int86x also places segregs. x. es and segregs. x. DS value is stored in the corresponding segment register). After Soft Interrupt is returned, both functions store the value of the current Register to outregs, and copy the system carry sign to outregs. s. in cflag, store the 8086 flag register value to outregs. x. flag (int86x also restores DS and sets segregs. es and segregs. DS value is the value of the corresponding segment register ).

Refer to the BIOS interrupt call manual and find that most calls do not use the ES and DS registers. Therefore, the int86 function is often used in programming.

2. Hardware interruption

To give readers an intuitive impression, we can view the resource properties of COM in the Windows operating system to get the interrupt number of a COM, 2 (enabled in the Device Manager in this dialog box ).

Figure 2 COM interrupt number

In fact, com directly corresponds to an interrupt, and the system also allocates a fixed interrupt number for various hardware according to certain rules, such as table 1.

Table 1 interrupted vector table

INT (Hex) IRQ Common Uses
08 0 System Timer
09 1 Keyboard
0A 2 Redirected
0B 3 Serial Comms. COM2/COM4
0C 4 Serial Comms. COM1/COM3
0D 5 Reserved/Sound Card
0E 6 Floppy Disk Controller
0F 7 Parallel Comms.
70 8 Real Time Clock
71 9 Reserved
72 10 Reserved
73 11 Reserved
74 12 PS/2 Mouse
75 13 Maths Co-Processor
76 14 Hard Disk Drive
77 15 Reserved

By compiling the interrupt service program corresponding to com, we can also operate the serial port. Related functions include:

(1) set the interrupt vector table

/* Dos. h */
Void _ Cdecl setvect (int interruptno, void interrupt (* isr )());

For example, if the interrupt Number of com3 is 4, the address of the corresponding interrupt vector table is 0x0c, and the function of the interrupt program corresponding to 0x0c is set:

Setvect (0x0C, PORT1INT );

The interrupt service program port1int is:

Void interrupt PORT1INT ()
{
Int c;
Do
{
C = inportb (port1 + 5 );
If (C & 1)
{
Buffer [Bufferin] = inportb (port1 );
Bufferin ++;
If (Bufferin = 1024)
Bufferin = 0;
}
}
While (C & 1 );
Outportb (0x20, 0x20 );
}

The above interrupt service program checks whether any character can be received, and then reads it from the UART through the inportb (port1) Statement and puts it into the input buffer. Continuously checks UART to read all available data in one interruption.

The final "outportb (0x20, 0x20);" statement tells the Programmable Interrupt Controller (PIC) that the interrupt has been completed.

(2) read the interrupted vector table

/* Dos. H */
Void interrupt (* _ cdecl getvect (INT interruptno ))();

For example:

Oldport1isr = getvect (intvect );

The oldport1isr is defined:

Void interrupt (* oldport1isr )();

We integrate the setvect () function, interrupt service program, and getvect () function to provide a complete routine compiled by Craig peaco CK:

/* Name: Sample comm's Program-1024 byte buffer-buff1024.c */
/* Written by: Craig peacock <cpeacock@senet.com.au> */
# Include <dos. h>
# Include <stdio. h>
# Include <conio. h>

# Define PORT1 0x3F8/* Port Address Goes Here */
# Define INTVECT 0x0C/* Com Port's IRQ here (Must also change PIC setting )*/

/* Defines Serial Ports Base Address */
/* COM1 0x3F8 */
/* COM2 0x2F8 */
/* COM3 0x3E8 */
/* COM4 0x2E8 */

Int bufferin = 0;
Int bufferout = 0;
Char ch;
Char buffer [1025];

Void interrupt (* oldport1isr )();

Void interrupt PORT1INT ()/* Interrupt Service Routine (ISR) for PORT1 */
{
Int c;
Do
{
C = inportb (PORT1 + 5 );
If (c & 1)
{
Buffer [bufferin] = inportb (PORT1 );
Bufferin ++;
If (bufferin = 1024)
{
Bufferin = 0;
}
}
}
While (c & 1 );
Outportb (0x20, 0x20 );
}

Void main (void)
{
Int c;
Outportb (PORT1 + 1, 0);/* Turn off interrupts-Port1 */

Oldport1isr = getvect (INTVECT);/* Save old Interrupt Vector of later
Recovery */

Setvect (INTVECT, PORT1INT);/* Set Interrupt Vector Entry */
/* COM1-0x0C */
/* COM2-0x0B */
/* COM3-0x0C */
/* COM4-0x0B */

/* PORT 1-Communication Settings */

Outportb (PORT1 + 3, 0x80);/* set dlab on */
Outportb (PORT1 + 0, 0x0C);/* Set Baud rate-Divisor Latch Low Byte */
/* Default 0 x 03= 38,400 BPS */
/* 0x01 = 115,200 BPS */
/* 0x02 = 57,600 BPS */
/* 0x06 = 19,200 BPS */
/* 0x0C = 9,600 BPS */
/* 0x18 = 4,800 BPS */
/* 0x30 = 2,400 BPS */
Outportb (PORT1 + 1, 0x00);/* Set Baud rate-Divisor Latch High Byte */
Outportb (PORT1 + 3, 0x03);/* 8 Bits, No Parity, 1 Stop Bit */
Outportb (PORT1 + 2, 0xC7);/* FIFO Control Register */
Outportb (PORT1 + 4, 0x0B);/* Turn on DTR, RTS, and OUT2 */

Outportb (0x21, (inportb (0x21) & 0xEF);/* Set Programmable Interrupt Controller */
/* COM1 (IRQ4)-0xEF */
/* COM2 (IRQ3)-0xF7 */
/* COM3 (IRQ4)-0xEF */
/* COM4 (IRQ3)-0xF7 */

Outportb (PORT1 + 1, 0x01);/* Interrupt when data has ed */

Printf ("/nSample Comm's Program. Press ESC to quit/n ");

Do
{
If (bufferin! = Bufferout)
{
Ch = buffer [bufferout];
Bufferout ++;
If (bufferout = 1024)
{
Bufferout = 0;
}
Printf ("% c", ch );
}

If (kbhit ())
{
C = getch ();
Outportb (PORT1, c );
}
}
While (c! = 27 );

Outportb (PORT1 + 1, 0 );
/* Turn off interrupts-Port1 */
Outportb (0x21, (inportb (0x21) | 0x10);/* mask irq using PIC */
/* COM1 (IRQ4)-0x10 */
/* COM2 (IRQ3)-0x08 */
/* COM3 (IRQ4)-0x10 */
/* COM4 (IRQ3)-0x08 */
Setvect (INTVECT, oldport1isr);/* Restore old interrupt vector */
}

 

3. Hardware Query

By reading and writing the hardware port corresponding to the serial UART, we can control the serial port sending and receiving. See the following example:

/* Name: Sample comm's Program-polled version-termpoll. C */
/* Written by: Craig peacock <cpeacock@senet.com.au> */
# Include <dos. h>
# Include <stdio. h>
# Include <conio. h>

00000000000000000000 # define PORT1 0x3F8

/* Defines Serial Ports Base Address */
/* COM1 0x3F8 */
/* COM2 0x2F8 */
/* COM3 0x3E8 */
/* COM4 0x2E8 */

Void main (void)
{
Int c;
Int ch;
Outportb (PORT1 + 1, 0);/* Turn off interrupts-Port1 */

/* PORT 1-Communication Settings */

Outportb (PORT1 + 3, 0x80);/* set dlab on */
Outportb (PORT1 + 0, 0x03);/* Set Baud rate-Divisor Latch Low Byte */
/* Default 0 x 03= 38,400 BPS */
/* 0x01 = 115,200 BPS */
/* 0x02 = 57,600 BPS */
/* 0x06 = 19,200 BPS */
/* 0x0C = 9,600 BPS */
/* 0x18 = 4,800 BPS */
/* 0x30 = 2,400 BPS */
Outportb (port1 + 1, 0x00);/* Set baud rate-divisor latch high byte */
Outportb (port1 + 3, 0x03);/* 8 bits, no parity, 1 stop bit */
Outportb (port1 + 2, 0xc7);/* FIFO control register */
Outportb (port1 + 4, 0x0b);/* Turn on DTR, RTS, and out2 */

Printf ("/nsample comm's program. Press ESC to quit/N ");

Do
{
C = inportb (port1 + 5);/* check to see if Char has been */
/* Received .*/
If (C & 1)
{
Ch = inportb (port1);/* if so, then get char */
Printf ("% C", CH );
}/* Print Char to screen */

If (kbhit ())
{
Ch = getch ();/* If key pressed, get char */
Outportb (port1, CH );
}/* Send Char to serial port */
}
While (ch! = 27);/* Quit when ESC (ASC 27) is pressed */
}

In the program

C = inportb (port1 + 5);/* check to see if Char has been */
/* Received .*/
If (C & 1)

Check the PORT1 + 5 port address and use c & 1 to determine whether data is received by UART. The port range corresponding to UART can be intuitively seen in figure 2.

Contact Us

The content source of this page is from Internet, which doesn't represent Alibaba Cloud's opinion; products and services mentioned on that page don't have any relationship with Alibaba Cloud. If the content of the page makes you feel confusing, please write us an email, we will handle the problem within 5 days after receiving your email.

If you find any instances of plagiarism from the community, please send an email to: info-contact@alibabacloud.com and provide relevant evidence. A staff member will contact you within 5 working days.

A Free Trial That Lets You Build Big!

Start building with 50+ products and up to 12 months usage for Elastic Compute Service

  • Sales Support

    1 on 1 presale consultation

  • After-Sales Support

    24/7 Technical Support 6 Free Tickets per Quarter Faster Response

  • Alibaba Cloud offers highly flexible support services tailored to meet your exact needs.