This is something of a followup to How much control of TRS-80 Model III disk drives was possible from its Cassette (ROM) BASIC? but on a more general topic:

I know that PEEK and POKE enable direct access the computer's memory (both ROM and RAM) from most BASIC implementations on the early home computers. POKE N,B and B = PEEK(N) are, respectively, like *n = b; and b = *n; in C and I suppose in either the BASIC case or the C case their exact behavior depends on what memory mapping is set up.

Many platforms expose not just memory, but all of their "peripherals" through special memory addresses. I think the PDP-11 was this way based on all the opcodes I've seen, I understand the Apple ][ may have been this way as well. Even more recently, chips like the MSP430 and the AVR series at the instruction set level all ultimately seem to use a single "memory" address space for accessing anything of the world outside the processor, both actual memory and any other I/O devices.

However, with at least the TRS-80 (were there other examples? was this Z80 family specific?) there is another type of I/O exposed through BASIC: the IN and OUT commands.

What did those actually do?

The user manuals say they read/write to "ports". Is that just a higher level abstraction of something also accessible through the memory space, or did some I/O actually happen through a different mechanism?

I did find for example on the Model III a list of Memory Mapped I/O Devices that has some apparent overlap with the Ports list. For example the memory address 37E4h is listed as the "Cassette drive latch" while FF is listed as the "Cassette Unit I/O" port. Is OUT 255,42 equivalent to POKE 14308,42 then?

  • I did find electronics.stackexchange.com/questions/92466/… but it's not clarifying it for me. The answers seem to say that "ports" are just the bottom 0–255 addresses, but that does NOT match up in my Model III cassette example for starters or explain why the IN/OUT commands exist separately from PEEK/POKE.
    – natevw
    Commented Jan 27, 2020 at 23:08
  • 2
    The correct term for the way the |PDP11 did IO is called "memory mapped IO". The 6502 is another well known example of a processor without special IO addresses.
    – JeremyP
    Commented Jan 28, 2020 at 8:38
  • I think the TRS-80 BASIC actually uses INP for the input command. I was getting strange ?BS Error results with e.g. ?IN(244) but ?INP(244) works fine — I'm assuming this must be a typo in Ira's site or something!
    – natevw
    Commented Feb 20, 2020 at 4:49

1 Answer 1


The TRS-80 series is Z80 based, and Z80 uses, like all 8080 offspring (*1,3) a separate address space for I/O. It allows easy decoding for I/O.

Thus memory address 0000h is different from I/O address 00h.

On logical (program) level, access to either address space is selected by the instructions used. Memory instructions always access memory address space and I/O do the same for I/O address space. On hardware level, it is marked by status signals. On a Z80 for example, an active /MREQ line signals memory access and thus a memory address on A0..15, while /IORQ does the same for I/O space.

In BASIC, PEEK and POKE are made to allow memory access. BASICs for x80/x86 CPUs therefore carry in addition IN and OUT instructions to allow access to ports (*4).

Other than having its own address space and instruction set for access, I/O does not differ from memory.

Historically having device specific I/O instructions, or more generically a separate I/O address space, was quite common and rather the norm. Making ports accessible via memory reference was out of the ordinary, requiring to add the adjective of 'memory mapped' to I/O. Off course, even on a CPU with a dedicated I/O space it is still possible to have memory mapped I/O. And that's what Tandy did for some of their interfaces. They were located in I/O space, but also mapped to memory as well.

The 8080/85 had an 8-bit I/O address space, while Z80 and later the 8086 extended this to 16-bit. Except, the Z80 handled 8-bit I/O addresses differently from 16-bit, and system design defines which is to be used.

*1 - In fact it goes back to the 8008 (*2), except here only 8 input and 24 output ports could be addressed, as they shared one instruction format with a single 5 bit address without a direction signal. To distinguish, the top two bits were set to 00 for input, and each input instruction also output A first. Unusual for today's mind, but quite capable - and compared with its predecessor almost streamlined :)

*2 - Which took it from the Datapoint 2200, using a solution even more outlandish when seen from today: here only a single input port could be read with the dedicated INPUT instruction and no output command, but instead an 'External Command' EX which presented A on the external command bus accompanied by a 5 bit command code.

*3 - Yes, that means all the way to modern x86 CPUs.

*4 - I/O addresses are usually called ports.

  • 2
    @another-dave Sure. Then again, caching wasn't much requested on a TRS-80 :))
    – Raffzahn
    Commented Jan 28, 2020 at 2:17
  • 1
    Well, there is that aspect :-) I was thinking 386 and later.
    – dave
    Commented Jan 28, 2020 at 2:28
  • 1
    @another-dave yes, of course, you're right about them. I was too much focused on Z80 despite having mentioned newer as well. Memory mapped I/O is a real pain in combination with caching. While it's easy to do no caching when it's a dedicated I/O instruction, modern CPUs need an awful lot of software support to not screw up memory mapped I/O. Not to mention optimizing compilers as well - that's why C had to get its VOLATILE keyword.
    – Raffzahn
    Commented Jan 28, 2020 at 2:58
  • 2
    For what it's worth: the Z80 only uses two pins to distinguish between I/O and memory access. There's an IORQ pin, which is asserted to read or write an I/O port, and a MEMREQ pin that's asserted to read or write memory (the 8085/8086/8088 used the more obvious design of a single pin that meant "I/O request" in one state, and "memory request" in the other state). Commented Jan 28, 2020 at 6:12
  • 3
    @Flydog57 "When data would arrive at a port, you'd get an interrupt" - I'd argue that this statement is not universally true. For example, ZX Spectrum, probably one of the most popular home computers built with Z-80, used an interrupt only for syncing with vertical screen blanking (OK, at least the tape version, not so sure about disk I/O). Either way, reading the keyboard or joystick input did not require any interrupts, and neither did reading data bits from a cassette tape. All timing was purely software delay loops.
    – DmytroL
    Commented Feb 3, 2020 at 11:40

You must log in to answer this question.

Not the answer you're looking for? Browse other questions tagged .