File Archive

 
Output of file : CHAP18.DOC contained in archive : ASMTUT3.ZIP




185

CHAPTER 18 - PORTS


In order to communicate with the outside world, the outside world
being your printer, your disk drives, your modem etc., the 8086
uses ports. A port is an address distinct from a memory address
where the i/o device can be reached.{1} When IBM set up the
first PC, they decided what these port addresses would be and
what the function of each bit at each address would be. Any VGA,
EGA, CGA or monochrome card has to have it's ports at the same
addresses as those set by IBM, and these ports have to do the
same thing.

Normally, an i/o device has more than one port address. The
device not only has to transfer the data, it has to tell the
computer whether it is ready, find out if the computer wants to
send information, confirm that the transfer was successful etc.
COM1 is port addresses 3F8 - 3FF. COM2 is 2F8 - 2FF. The CGA
adapter is ports 3D0 - 3DF.

In the CGA, port 3D9 sets the different colors. The 8086 writes
to it, but can't read it. Port 3DA, it's neighbor, tells certain
status information and is read only.

All control information for the video cards is passed back and
forth through the ports. However, the video card comes into
memory 50 times a second to see what it should write to the
screen. The characters on the screen don't pass through the
ports. The mountain comes to Mohammed.

The data does pass through a port on the way to your serial
printer. The 8086 sends your printer control information through
one port address and sends the data through a different port
address.

The port instructions can be either with AL or AX. AL and AX are
the only registers you can use, AL for a byte transfer and AX for
a word transfer. There are two forms to the input instruction:

in al, port_address
or
in al, dx

port_address is a constant and is limited to 0 - 255. It is a one
byte constant. DX is the only register than can be used with the
second form. DX can contain any number from 0 - 65535. The
____________________

1. There is memory address 0000 and there is port address
0000; they are two entirely different things. You can reach
memory address 0000 with the DS:SI pair 0000:0000, but DS:SI
can't get to port address 0000. The instruction "out 0, al"
writes the contents of al to port address 0000, but the OUT
instruction can't get anywhere near memory address 0000.

______________________

The PC Assembler Tutor - Copyright (C) 1989 Chuck Nelson




The PC Assembler Tutor 186
______________________

following two pieces of code do the same thing:

in al, 155

mov dx, 155
in al, dx

This code moves one byte of data from port 155 to register AL.

The output instruction is similar. We have:

out port_address, al
or
out dx, al

where port_address is a constant 0-255 and DX can contain a
number 0 - 65535.

out 97, ax

moves a word from AX to port addresses 97-98. Notice that both
the IN and the OUT instructions follow the DESTINATION, SOURCE
ordering found in all the other 8086 instructions.

The "in ax, port_address" form is not all that useful. If you
look at the addresses for the video ports, for COM1 and COM2, you
will see that they are all larger than 255. Also, most equipment
can be at several different addresses. A modem can be at COM1,
COM2, COM3, or COM4. If the port address is hard coded into the
instructions, you need 4 subprograms to handle the 4 different
addresses. It is easier to find out where the modem is, then use:

modem_data_address dw ?

mov dx, modem_data_address
in al, dx

and
mov dx, modem_data_address
out dx, al

If you aren't planning on writing device drivers this is for
background information only, since all standard i/o is done
through DOS or BIOS interrupts.

One last thing is the parity flag. It has been sitting on the
screen with all the other flags. What is it for? When you send
information over a modem, there is a large possibility of line
interference. If it is text, and an occasional screw up is not
too bad, using a parity check may be enough.{2} Parity can be
even or odd. Even means that an even number of bits are set to 1;
odd means that an odd number of bits are set to 1. This is not
whether the number is even or odd, it is whether the number of 1
____________________

2. It is NEVER enough if you are transferring binary data or
executable files.




Chapter 18 - Ports 187
__________________

BITS is even or odd. Here's a short list.

NUMBER BINARY PARITY

6 0110 even
7 0111 odd
8 1000 odd
9 1001 even
10 1010 even

8 is an even number but has odd parity, 9 is an odd number but
has even parity. 6 has two 1 bits (even), 7 has three 1 bits
(odd), 8 has one 1 bit (odd), 9 has two 1 bits (even) and 10 has
two 1 bits (even).

How does this help us? If there is a chance of 1/100 of screwing
up a single bit in a byte, there is only a chance of 1/10,000 of
screwing up two bits in a byte. What this means is that of every
100 errors, 99 of them will will be detectable because of a
change in parity (by changing a bit from 0 to 1 or from 1 to 0)
and only 1 of them will go undetected because two changes will
keep the same parity. This is a little obscure, so make sure you
understand why before continuing.

This doesn't help us much yet, because we don't know what the
parity was originally. 9 has even parity, 7 has odd parity. What
communications programs do is FORCE the parity. They can either
force it even or force it odd.

The standard ASCII characters end at 127 - that is, 0111 1111
(7F) is the highest legal number you can transmit. The left bit
is unused, so we can use it to force the parity. We are going to
force it even, but forcing it odd uses the same technique.

The steps are:

(1) Find the parity of the byte.

(2) If it is even, leave it alone, if it is odd, put a 1 in
the left hand bit. The parity is now even.

If both the sending and receiving program have agreed on even
parity, then on the receiving end, the program:

(1) Checks for even parity. If the parity is odd, it is an
error.

(2) Puts a 0 back in the left hand bit. The original number
is restored.

We are going to make a loop with both parts and use show_regs to
watch the parity flag. Here's the program:

; - - - - - - - - - - ENTER DATA BELOW THIS LINE

error_banner db "Whoa! We have a screw up.", 13, 10, 0





The PC Assembler Tutor 188
______________________

; - - - - - - - - - - ENTER DATA ABOVE THIS LINE

; - - - - - - - - - - ENTER CODE BELOW THIS LINE
mov ax_byte, 0A3h ; al binary
mov bx_byte, 0A2h ; unsigned half registers
mov cx_byte, 0A2h ; unsigned half registers
mov dx_byte, 0A3h ; dl binary
lea ax, ax_byte
call set_reg_style

comm_loop:
mov ax, 0
call set_count ; reset count to 0
mov bx, 0 ; clear registers
mov cx, 0
mov dx, 0
call show_regs ; (1)

; the sending program

call get_unsigned_byte
and al, 7Fh ; 0111 1111 - make sure al < 128
mov bl, al ; copy to bl
call show_regs_and_wait ; (2)

and al, al ; check parity
call show_regs_and_wait ; (3)

jpe do_nothing
or al, 80h ; if parity odd, set left bit

do_nothing:
and al, al ; check parity for show_regs
call show_regs_and_wait ; (4)

; the receiving program

mov dl, al ; transmit from al to dl
mov cl, dl ; copy to cl
call show_regs_and_wait ; (5)

and dl, dl
jpe zero_left_bit ; is parity even?
mov ax, error_banner
call print_string

zero_left_bit:
and dl, 7Fh ; 0111 1111 binary, left bit 0
mov cl, dl ; copy to cl
call show_regs_and_wait ; (6)
jmp comm_loop

; - - - - - - - - - - ENTER CODE ABOVE THIS LINE

First, we set the register style so AL and DL are binary, BL and
CL are unsigned. We'll use AL and BL for sending, CL and DL for
receiving. Next, we reset the counter to 0 so we can see where we




Chapter 18 - Ports 189
__________________

are, and zero AX, BX, CX, and DX. We get a byte and make sure
that it is 127 or less,{3} then send a copy to BL. BL stays
unchanged for the rest of the loop. We test the parity. and if it
is odd, change it. Finally, we send it off to DL.

On the receiving end, we test the parity. If it is odd, we print
an error message. Then we zero the left hand bit. It is faster to
zero the left hand bit than to check to see if it needs to be
zeroed, so we do it for all data. The result is put in CL for
comparison. AL and DL are shown in binary form so you can count
the number of 1 bits in the byte.

































____________________

3. The following could happen if we didn't take this step. We
get a number > 128 with odd parity. 131 (1000 0011 83h) is an
example. The sending program sees that it is odd parity, so it
puts a 1 in the left hand bit. But there is already a 1 in the
left hand bit, so the parity doesn't change, it is odd. The
receiving program gets the byte, tests it, notices that it is
odd, and sends back a transmission error. Since the sending
program didn't see anything wrong, it just sends the same byte
again. It will never get through correctly. In real life, the
sending program would probably test the byte to see if it was
greater than 127 and print an error if it was.




The PC Assembler Tutor 190
______________________

SUMMARY


POSSIBLE I/O INSTRUCTIONS

in ax, constant (= port address 0 - 255)
in ax, DX

in al, constant (= port address 0 - 255)
in al, DX

out constant, ax (constant = port address 0 - 255)
out DX, ax

out constant, al (constant = port address 0 - 255)
out DX, al

In these instructions DX holds a port address and
can be from 0 - 65535.


PARITY

Parity is whether the number of 1 bits in a byte (or word) is
even or odd. The 8086 sets the parity flag after most arithmetic
and all logical operations. If parity is even, the flag is 1, if
parity is odd, the flag is 0.