The Art of
ASSEMBLY LANGUAGE PROGRAMMING

Chapter Thirteen (Part 2)

Table of Content

Chapter Thirteen (Part 4) 

CHAPTER THIRTEEN:
MS-DOS, PC-BIOS AND FILE I/O (Part 3)
13.2.6 - INT 14h - Serial I/O
13.2.6.1 - AH=0: Serial Port Initialization
13.2.6.2 - AH=1: Transmit a Character to the Serial Port
13.2.6.3 - AH=2: Receive a Character from the Serial Port
13.2.6.4 - AH=3: Serial Port Status
13.2.7 - INT 15h - Miscellaneous Services
13.2.8 - INT 16h - Keyboard Services
13.2.8.1 - AH=0: Read a Key From the Keyboard
13.2.8.2 - AH=1: See if a Key is Available at the Keyboard
13.2.8.3 - AH=2: Return Keyboard Shift Key Status
13.2.9 - INT 17h - Printer Services
13.2.9.1 - AH=0: Print a Character
13.2.9.2 - AH=1: Initialize Printer
13.2.9.3 - AH=2: Return Printer Status
13.2.10 - INT 18h - Run BASIC
13.2.11 - INT 19h - Reboot Computer
13.2.12 - INT 1Ah - Real Time Clock
13.2.12.1 - AH=0: Read the Real Time Clock
13.2.12.2 - AH=1: Setting the Real Time Clock

13.2.6 INT 14h - Serial I/O

Instruction:    int 14h
BIOS Operation: Access the serial communications port
Parameters:     ax, dx

The IBM BIOS supports up to four different serial communications ports (the hardware supports up to eight). In general, most PCs have one or two serial ports (COM1: and COM2:) installed. Int 14h supports four subfunctions- initialize, transmit a character, receive a character, and status. For all four services, the serial port number (a value in the range 0..3) is in the dx register (0=COM1:, 1=COM2:, etc.). Int 14h expects and returns other data in the al or ax register.

13.2.6.1 AH=0: Serial Port Initialization

Subfunction zero initializes a serial port. This call lets you set the baud rate, select parity modes, select the number of stop bits, and the number of bits transmitted over the serial line. These parameters are all specified by the value in the al register using the following bit encodings:

Bits    Function 
5..7    Select baud rate
                000- 110 baud
                001- 150
                010- 300
                011- 600
                100- 1200
                101- 2400
                110- 4800
                111- 9600

3..4    Select parity
                00- No parity
                01- Odd parity
                10- No parity
                11- Even parity

2       Stop bits
                 0-One stop bit
                 1-Two stop bits
0..1    Character Size
                 10- 7 bits
                 11- 8 bits

Although the standard PC serial port hardware supports 19,200 baud, some BIOSes may not support this speed.

Example: Initialize COM1: to 2400 baud, no parity, eight bit data, and two stop bits-

                mov     ah, 0           ;Initialize opcode
                mov     al, 10100111b   ;Parameter data.
                mov     dx, 0           ;COM1: port.
                int     14h

After the call to the initialization code, the serial port status is returned in ax (see Serial Port Status, ah=3, below).

13.2.6.2 AH=1: Transmit a Character to the Serial Port

This function transmits the character in the al register through the serial port specified in the dx register. On return, if ah contains zero, then the character was transmitted properly. If bit 7 of ah contains one, upon return, then some sort of error occurred. The remaining seven bits contain all the error statuses returned by the GetStatus call except time out error (which is returned in bit seven). If an error is reported, you should use subfunction three to get the actual error values from the serial port hardware.

Example: Transmit a character through the COM1: port

                mov     dx, 0           ;Select COM1:
                mov     al, 'a'         ;Character to transmit
                mov     ah, 1           ;Transmit opcode
                int     14h
                test    ah, 80h         ;Check for error
                jnz     SerialError

This function will wait until the serial port finishes transmitting the last character (if any) and then it will store the character into the transmit register.

13.2.6.3 AH=2: Receive a Character from the Serial Port

Subfunction two is used to read a character from the serial port. On entry, dx contains the serial port number. On exit, al contains the character read from the serial port and bit seven of ah contains the error status. When this routine is called, it does not return to the caller until a character is received at the serial port.

Example: Reading a character from the COM1: port

                mov     dx, 0           ;Select COM1:
                mov     ah, 2           ;Receive opcode
                int     14h
                test    ah, 80h         ;Check for error
                jnz     SerialError 

        <Received character is now in AL>
13.2.6.4 AH=3: Serial Port Status

This call returns status information about the serial port including whether or not an error has occurred, if a character has been received in the receive buffer, if the transmit buffer is empty, and other pieces of useful information. On entry into this routine, the dx register contains the serial port number. On exit, the ax register contains the following values:

AX:     Bit Meaning 
15      Time out error
14      Transmitter shift register empty
13      Transmitter holding register empty
12      Break detection error
11      Framing error
10      Parity error
9       Overrun error
8       Data available
7       Receive line signal detect 
6       Ring indicator
5       Data set ready (DSR)
4       Clear to send (CTS)
3       Delta receive line signal detect
2       Trailing edge ring detector
1       Delta data set ready
0       Delta clear to send

There are a couple of useful bits, not pertaining to errors, returned in this status information. If the data available bit is set (bit #8), then the serial port has received data and you should read it from the serial port. The Transmitter holding register empty bit (bit #13) tells you if the transmit operation will be delayed while waiting for the current character to be transmitted or if the next character will be immediately transmitted. By testing these two bits, you can perform other operations while waiting for the transmit register to become available or for the receive register to contain a character.

If you're interested in serial communications, you should obtain a copy of Joe Campbell's C Programmer's Guide to Serial Communications. Although written specifically for C programmers, this book contains a lot of information useful to programmers working in any programming language. See the bibliography for more details.

13.2.7 INT 15h - Miscellaneous Services

Originally, int 15h provided cassette tape read and write services[1]. Almost immediately, everyone realized that cassettes were history, so IBM began using int 15h for many other services. Today, int 15h is used for a wide variety of function including accessing expanded memory, reading the joystick/game adapter card, and many, many other operations. Except for the joystick calls, most of these services are beyond the scope of this text. Check on the bibliography if you interested in obtaining information on this BIOS call.

13.2.8 INT 16h - Keyboard Services

Instruction:    int 16h
BIOS Operation: Read a key, test for a key, or get keyboard status
Parameters:     al

The IBM PC BIOS provides several function calls dealing with the keyboard. As with many of the PC BIOS routines, the number of functions has increased over the years. This section describes the three calls that were available on the original IBM PC.

13.2.8.1 AH=0: Read a Key From the Keyboard

If int 16h is called with ah equal to zero, the BIOS will not return control to the caller until a key is available in the system type ahead buffer. On return, al contains the ASCII code for the key read from the buffer and ah contains the keyboard scan code. Keyboard scan codes are described in the appendices.

Certain keys on the PC's keyboard do not have any corresponding ASCII codes. The function keys, Home, PgUp, End, PgDn, the arrow keys, and the Alt keys are all good examples. When such a key is pressed, int 16h returns a zero in al and the keyboard scan code in ah. Therefore, whenever an ASCII code of zero is returned, you must check the ah register to determine which key was pressed.

Note that reading a key from the keyboard using the BIOS int 16h call does not echo the key pressed to the display. You have to call putc or use int 10h to print the character once you've read it if you want it echoed to the screen.

Example: Read a sequence of keystrokes from the keyboard until Enter is pressed.

ReadLoop:       mov     ah, 0           ;Read Key opcode
                int     16h
                cmp     al, 0           ;Special function?
                jz      ReadLoop        ;If so, don't echo this keystroke
                putc
                cmp     al, 0dh         ;Carriage return (ENTER)?
                jne     ReadLoop
13.2.8.2 AH=1: See if a Key is Available at the Keyboard

This particular int 16h subfunction allows you to check to see if a key is available in the system type ahead buffer. Even if a key is not available, control is returned (right away!) to the caller. With this call you can occasionally poll the keyboard to see if a key is available and continue processing if a key hasn't been pressed (as opposed to freezing up the computer until a key is pressed).

There are no input parameters to this function. On return, the zero flag will be clear if a key is available, set if there aren't any keys in the type ahead buffer. If a key is available, then ax will contain the scan and ASCII codes for that key. However, this function will not remove that keystroke from the typeahead buffer. Subfunction #0 must be used to remove characters. The following example demonstrates how to build a random number generator using the test keyboard function:

Example: Generating a random number while waiting for a keystroke

; First, clear any characters out of the type ahead buffer

ClrBuffer:      mov     ah, 1           ;Is a key available?
                int     16h
                jz      BufferIsClr     ;If not, Discontinue flushing
                mov     ah, 0           ;Flush this character from the
                int     16h             ; buffer and try again.
                jmp     ClrBuffer

BufferIsClr:    mov     cx, 0           ;Initialize "random" number.
GenRandom:      inc     cx
                mov     ah, 1           ;See if a key is available yet.
                int     16h
                jz      GenRandom
                xor     cl, ch          ;Randomize even more.
                mov     ah, 0           ;Read character from buffer
                int     16h

; Random number is now in CL, key pressed by user is in AX

While waiting for a key, this routine is constantly incrementing the cx register. Since human beings cannot respond rapidly (at least in terms of microseconds) the cl register will overflow many times, even for the fastest typist. As a result, cl will contain a random value since the user will not be able to control (to better than about 2ms) when a key is pressed.

13.2.8.3 AH=2: Return Keyboard Shift Key Status

This function returns the state of various keys on the PC keyboard in the al register. The values returned are as follows:

Bit     Meaning 
7       Insert state (toggle by pressing INS key)
6       Caps lock (1=capslock on)
5       Num lock (1=numlock on)
4       Scroll lock (1=scroll lock on)
3       Alt (1=Alt key currently down)
2       Ctrl (1=Ctrl key currently down)
1       Left shift (1=left shift key down)
0       Right shift (1=right shift key down)

Due to a bug in the BIOS code, these bits only reflect the current status of these keys, they do not necessarily reflect the status of these keys when the next key to be read from the system type ahead buffer was depressed. In order to ensure that these status bits correspond to the state of these keys when a scan code is read from the type ahead buffer, you've got to flush the buffer, wait until a key is pressed, and then immediately check the keyboard status.

13.2.9 INT 17h - Printer Services

Instruction:    int 17h
BIOS Operation: Print data and test the printer status
Parameters:     ax, dx

Int 17h controls the parallel printer interfaces on the IBM PC in much the same way the int 14h controls the serial ports. Since programming a parallel port is considerably easier than controlling a serial port, using the int 17h routine is somewhat easier than using the int 14h routines.

Int 17h provides three subfunctions, specified by the value in the ah register. These subfunctions are:

0-Print the character in the AL register.

1-Initialize the printer.

2-Return the printer status.

Each of these functions is described in the following sections.

Like the serial port services, the printer port services allow you to specify which of the three printers installed in the system you wish to use (LPT1:, LPT2:, or LPT3:). The value in the dx register (0..2) specifies which printer port is to be used.

One final note- under DOS it's possible to redirect all printer output to a serial port. This is quite useful if you're using a serial printer. The BIOS printer services only talk to parallel printer adapters. If you need to send data to a serial printer using BIOS, you'll have to use int 14h to transmit the data through a serial port.

13.2.9.1 AH=0: Print a Character

If ah is zero when you call int 17h, then the BIOS will print the character in the al register. Exactly how the character code in the al register is treated is entirely up to the printer device you're using. Most printers, however, respect the printable ASCII character set and a few control characters as well. Many printers will also print all the symbols in the IBM/ASCII character set (including European, line drawing, and other special symbols). Most printers treat control characters (especially ESC sequences) in completely different manners. Therefore, if you intend to print something other than standard ASCII characters, be forewarned that your software may not work on printers other than the brand you're developing your software on.

Upon return from the int 17h subfunction zero routine, the ah register contains the current status. The values actually returned are described in the section on subfunction number two.

13.2.9.2 AH=1: Initialize Printer

Executing this call sends an electrical impulse to the printer telling it to initialize itself. On return, the ah register contains the printer status as per function number two.

13.2.9.3 AH=2: Return Printer Status

This function call checks the printer status and returns it in the ah register. The values returned are:

AH:     Bit Meaning 
7       1=Printer busy, 0=printer not busy
6       1=Acknowledge from printer
5       1=Out of paper signal
4       1=Printer selected
3       1=I/O error
2       Not used
1       Not used
0       Time out error

Acknowledge from printer is, essentially, a redundant signal (since printer busy/not busy gives you the same information). As long as the printer is busy, it will not accept additional data. Therefore, calling the print character function (ah=0) will result in a delay.

The out of paper signal is asserted whenever the printer detects that it is out of paper. This signal is not implemented on many printer adapters. On such adapters it is always programmed to a logic zero (even if the printer is out of paper). Therefore, seeing a zero in this bit position doesn't always guarantee that there is paper in the machine. Seeing a one here, however, definitely means that your printer is out of paper.

The printer selected bit contains a one as long as the printer is on-line. If the user takes the printer off-line, then this bit will be cleared.

The I/O error bit contains a one if some general I/O error has occurred.

The time out error bit contains a one if the BIOS routine waited for an extended period of time for the printer to become "not busy" yet the printer remained busy.

Note that certain peripheral devices (other than printers) also interface to the parallel port, often in addition to a parallel printer. Some of these devices use the error/status signal lines to return data to the PC. The software controlling such devices often takes over the int 17h routine (via a technique we'll talk about later on) and always returns a "no error" status or "time out error" status if an error occurs on the printing device. Therefore, you should take care not to depend too heavily on these signals changing when you make the int 17h BIOS calls.

13.2.10 INT 18h - Run BASIC

Instruction:    int 18h
BIOS Operation: Activate ROM BASIC
Parameters:     None 

Executing int 18h activates the ROM BASIC interpreter in an IBM PC. However, you shouldn't use this mechanism to run BASIC since many PC compatibles do not have BASIC in ROM and the result of executing int 18h is undefined.

13.2.11 INT 19h - Reboot Computer

Instruction:    int 19h 
BIOS Operation: Restart the system
Parameters:     None 

Executing this interrupt has the same effect as pressing control-alt-del on the keyboard. For obvious reasons, this interrupt service should be handled carefully!

13.2.12 INT 1Ah - Real Time Clock

Instruction:    int 1ah
BIOS Operation: Real time clock services
Parameters:     ax, cx, dx

There are two services provided by this BIOS routine- read the clock and set the clock. The PC's real time clock maintains a counter that counts the number of 1/18ths of a second that have transpired since midnight. When you read the clock, you get the number of "ticks" which have occurred since then. When you set the clock, you specify the number of "ticks" which have occurred since midnight. As usual, the particular service is selected via the value in the ah register.

13.2.12.1 AH=0: Read the Real Time Clock

If ah = 0, then int 1ah returns a 33-bit value in al:cx:dx as follows:

Reg     Value Returned 
dx      L.O. word of clock count
cx      H.O. word of clock count
al      Zero if timer has not run for more than 24 hours
        Non-zero otherwise.

The 32-bit value in cx:dx represents the number of 55 millisecond periods which have elapsed since midnight.

13.2.12.2 AH=1: Setting the Real Time Clock

This call allows you to set the current system time value. cx:dx contains the current count (in 55ms increments) since last midnight. Cx contains the H.O. word, dx contains the L.O. word.


[1] For those who do not remember that far back, before there were hard disks people used to use only floppy disks. And before there were floppy disks, people used to use cassette tapes to store programs and data. The original IBM PC was introduced in late 1981 with a cassette port. By early 1982, no one was using cassette tape for data storage.

Chapter Thirteen (Part 2)

Table of Content

Chapter Thirteen (Part 4) 

Chapter Thirteen: MS-DOS, PC-BIOS and File I/O (Part 3)
28 SEP 1996