• Please review our updated Terms and Rules here

16-bit port I/O and the 8088

Mike Chambers

Veteran Member
Joined
Sep 2, 2006
Messages
2,621
There's something I've been trying to figure out and can't seem to find any solid info on. What happens on the 8088 if a 16-bit port read or write is performed, since the CPU only has an 8-bit external data bus? I would have thought that it would send the low byte followed by the high byte to the same port. Is this correct, or does it send the low byte to the port and then the high byte to the next highest port?
 
Mike, it works the same way for memory and I/O ports. The low-order byte <=> address, then high-order byte <=> address+1. This is the basis for the so-called "chuck mod" to the XTIDE.

It figures really--the BIU works the same way for both memory and I/O address spaces.
 
Mike, it works the same way for memory and I/O ports. The low-order byte <=> address, then high-order byte <=> address+1. This is the basis for the so-called "chuck mod" to the XTIDE.

It figures really--the BIU works the same way for both memory and I/O address spaces.

Great, thanks for the info. Yeah, that does make perfect sense actually now that I think about it. There's no difference between the memory and port bus except there's a line telling the peripherals which type of signal it is.
 
The behavior changes when you go to the 16 bit buses. Then you can do a real 16 bit transfer to a single port, and not have it go 8 bits to one port and 8 bits to another. The behavior difference is kind of annoying ...
 
The behavior changes when you go to the 16 bit buses. Then you can do a real 16 bit transfer to a single port, and not have it go 8 bits to one port and 8 bits to another. The behavior difference is kind of annoying ...

That's a good point. So an 8-bit ISA card wouldn't be able to do 16-bit port I/O correctly on a 16-bit bus then? I've used 8-bit cards in 16-bit slots many times, so I guess it's up to the software interfacing with them to handle both situations then right? I've never had to do that kind of programming yet. I was wondering about the behavior because I'm trying to make sure Fake86 does this properly.
 
That's a good point. So an 8-bit ISA card wouldn't be able to do 16-bit port I/O correctly on a 16-bit bus then? I've used 8-bit cards in 16-bit slots many times, so I guess it's up to the software interfacing with them to handle both situations then right? I've never had to do that kind of programming yet. I was wondering about the behavior because I'm trying to make sure Fake86 does this properly.

I'm not sure if the BIU on the 16 bit chips can figure out that it is talking to an 8 bit card and react accordingly. But yes, this is why the XT-IDE used two different registers for the data access instead of one like on a normal IDE controller. (And the "Chuck mod" rearranges the ports slightly to get the two data registers back-to-back, which then allows the use of a single 16 bit port I/O instruction to get the data. The BIU breaks it up into the two port accesses automatically, improving the speed when compared to the slow code that has to load port addresses for each 8 bit access.)
 
It's strictly a function of the BIU. If you output a word to an odd-numbered I/O port on an 8086, it's done in two operations using A0 and BHE/ as it would if you were writing a word to a memory address. The problem is that very few I/O devices actually pay attention to BHE/. If you output a word to an 8-bit device on the ISA bus, the motherboard runs it in two cycles.

The 5170 and later systems with 8-bit ISA slots have quite a bit of logic on the motherboard making sure that 8-bit accesses come off transparently. The reason why this works is that the IOCS16 signal on the second bus connector is asserted by the 16-bit device when selected to tell the motherboard logic that a 16-bit I/O port is being addressed. This is why, for example, that some 16-bit cards can operate just fine in an 8-bit slot and why all 8-bit cards will work in a 16-bit slot.

Some other 808x-based architectures do away with the problem by saying that an I/O port always resides on an even address, period. Considering that only 10 bits of the I/O address are decoded by most ISA peripherals, it would have seemed the logical thing to do on the 5150/5160 as there was certainly enough I/O address space. It certainly would have simplified a lot.

Reading/writing a word to an odd address on other CPUs (e.g. Motorola 68K) is simply illegal and throws an exception. Again, that makes a lot of sense if you consider the alternative of having to run two bus cycles. This feature/behavior is also an option on the 486 and later chips (cf the AC bit in EFLAGS and the AM bit in CR0).
 
Last edited:
I guess access to odd I/O ports in 8086/8088 was implemented for compatibility with 8-bit systems, which was making a lot of sense in late 70's. And probably helped people that had S-100 systems, and were able simply (not sure if 'simply' is a good word to use with S-100...) to switch CPU boards from 8-bit 8080/Z80 to 16-bit 8086.

BTW, when designing my XT board, I played with the prototype and tried to make it more 16-bit friendly (specifically to enable using some 16-bit VGA cards). I added circuitry that simulated a 16-bit I/O using two 8-bit operations (as 8088 does), but asserting /BHE and using upper 8-bit on ISA slot for odd addresses. Similarly to what an AT would do when even/odd I/O ports would be accessed one after another.

Well, it actually made one VGA card (if I remember correctly it was using a Realtek chip) work, but it broke otherwise properly functioning (in 8-bit mode) Trident TVGA8900/9000 and Cirrus Logic 542x cards, and didn't help with IDE (which simply doesn't have /BHE signal) so I decided against using this circuit in the production version.

Many 16-bit ISA cards (VGA, SCSI, Ethernet) would use 16-bit for I/O and RAM, but 8-bit (won't assert /MEMCS16) for BIOS extension ROM.
 
Last edited:
I guess in 8086/8088 odd I/O ports access implemented for compatibility with 8-bit systems, which made complete sense in late 70's. And probably helped a lot people that had S-100 systems, and were simply able to switch CPU boards from 8-bit 8080/Z80 to 16-bit 8086..

Given the differences in architecture, I suspect that very little 8085/Z80 device-driver code was automatically ported over to the 8088. Even though Intel was still selling what amounted to peripheral chips for the 8085 for 8088/8086 use, it was pretty much accepted practice that I/O drivers were to be reworked manually.
 
Given the differences in architecture, I suspect that very little 8085/Z80 device-driver code was automatically ported over to the 8088. Even though Intel was still selling what amounted to peripheral chips for the 8085 for 8088/8086 use, it was pretty much accepted practice that I/O drivers were to be reworked manually.

Right, that's why I put that remark about 'simply' :). But yet it was possible to reuse older I/O boards that could have I/O ports at odd addresses.
 
BTW, when designing my XT board, I played with the prototype and tried to make it more 16-bit friendly (specifically to enable using some 16-bit VGA cards). I added circuitry that simulated a 16-bit I/O using two 8-bit operations (as 8088 does), but asserting /BHE and using upper 8-bit on ISA slot for odd addresses. Similarly to what an AT would do when even/odd I/O ports would be accessed one after another.

Your XT board is totally awesome :) If you're tweaking things, how about looking at the DMA logic? It looks to me that just one additional logic gate is needed to make memory-to-memory transfer work.
 
Your XT board is totally awesome :) If you're tweaking things, how about looking at the DMA logic? It looks to me that just one additional logic gate is needed to make memory-to-memory transfer work.

I am open to any suggestions (especially that now I am working on an improved version). Can you please describe your recommendation in more details? :) I hope it won't break IBM PC compatibility?
 
The behavior changes when you go to the 16 bit buses. Then you can do a real 16 bit transfer to a single port, and not have it go 8 bits to one port and 8 bits to another. The behavior difference is kind of annoying ...

I thought that the CPU will always perform a data transfer equal to it's bus width for practical reasons. Since each port/memory address is DEFINED to be 8-bit word size in the IBM PC and beyond, wouldn't a 16-bit transfer always get two memory addresses/ports worth of data and shift/mask the unnecessary bits? What am I misunderstanding? I know I'm probably missing something, I'm just not sure what :confused:...
 
I thought that the CPU will always perform a data transfer equal to it's bus width for practical reasons. Since each port/memory address is DEFINED to be 8-bit word size in the IBM PC and beyond, wouldn't a 16-bit transfer always get two memory addresses/ports worth of data and shift/mask the unnecessary bits? What am I misunderstanding? I know I'm probably missing something, I'm just not sure what :confused:...

I think what happens is that on the 8086, the low (lines D0 to D7) and high (D8 to D15) data bits for memory are actually stored in two separate 8-bit banks. The BIU automatically handles the accesses based on if the memory location is odd or even. It requires an extra access cycle for the high byte if it's aligned on an odd address because the address lines must change to read/write it properly. This is why word aligned 16-bit memory accesses are faster with an 8086 than on an 8088, but it's the same speed when it's not aligned.

I have no idea why they didn't do the same thing for port I/O. I guess it doesn't make as much sense to do it with a peripheral's data ports as it does with regular memory.
 
Last edited:
I thought that the CPU will always perform a data transfer equal to it's bus width for practical reasons. Since each port/memory address is DEFINED to be 8-bit word size in the IBM PC and beyond, wouldn't a 16-bit transfer always get two memory addresses/ports worth of data and shift/mask the unnecessary bits? What am I misunderstanding? I know I'm probably missing something, I'm just not sure what :confused:...

The technique you described won't work with I/O devices or memory writes, because CPU doesn't really know what goes into "unused" parts of the word, and with I/O because unintended access (even read) can affect I/O device state.

Some 16-bit architectures access memory and I/O ports strictly using 16-bit words (for example PDP-11). But it's completely not the case for x86 architecture. x86 can do 8-bit, 16-bit, 32-bit (for 386 and later) memory and I/O accesses, and 64-bit (Pentium and above*) memory accesses.
To perform this, x86 CPUs have "byte enable" control lines, that determine what bytes are to be addressed within one word (word is 2 bytes for 8086, 286 and 386SX, 4 bytes for 386DX and 486, 8 bytes for Pentium)...
In x86 it is possible that the addressed "logical" word (16-bit or 32-bit in 386 or later) crosses physical word boundary, in this case CPU performs two access cycles - one for lower part of the word and one for the higher part.

Additionally IBM AT and compatibles include data bus steering logic that handles the case where I/O or memory data path width is smaller than the width CPU data path. This logic splits one CPU's access cycle into two (or 4 in case of 32-bit) access cycles. For example if you have a 8-bit extension ROM connected to the 386 motherboard, and a program tries to read a 32-bit word from there, it will be split into four successive 8-bit read accesses.

*Pentium and some later x86 CPUs, while technically being 32-bit CPUs use 64-bit data path for memory access. This is to achieve better throughput.
 
Well, it appears I have some summer reading to do on the x86 and its BIU :p. I never realized that having an external datapath a different width from the internal datapath would result in such headaches (like everything else in x86).
 
Last edited:
Back
Top