How did the Apple II decode the addresses for the expansion cards?

From The Apple Story:

Alan Baum helped a lot. I already had some address decoders on the board that decoded every 16th and every 256th I/O address. Alan realized very clearly that each card could have its own little 256-byte PROM on it and how they could all share a bank-switched 2K space. So I could just send two decoding signals to each card. Each board had 16 addresses for its I/O and 256 bytes of space for its PROM. Alan was the most constructive person in terms of realizing very clearly just how well the hardware-software interaction would work.

Also in that interview, there is a remark about how on S-100 systems, the cards are more complex designs because each one is responsible of decoding its own address; but the Apple II was simpler because the computer itself decoded the addresses.

I am wondering how this worked. What is the 256 byte PROM for? Does that contain executable code, something like a device driver?

Why does each card get two "decoding signals"? Are these to select somehow which I/O addresses are read from/written to?


2 Answers 2


The Apple II employs what's called a geographic addressing. That is each expansion slot gets a fixed address (range) reserved and assigned for local resources. Thus the address of a component (like a VIA) changes, depending on which slot is used. Geographic addressing is the most simple form of plug and play while retaining the ability to have multiple instances of a certain I/O device.

One primary goal of the Apple II I/O design was to enable card builders to create new cards with the least possible effort. Geographic addressing allows this, as there is, at least for simple cards, no need for additional decoding on the card, as the slot specific select line is all that needs to be used.

For example a 6522 VIA card could be built without any additional circuitry but the chip itself. Slot lines could be directly wired up to the IC with the lower 4 address lines tied to the register address input and /DEVSEL to chip enable. Similarly most other I/O chips of that time could be added.

For such simple I/O, each slot got a 16 byte address range for registers set aside. In a more general view it is preferable if another address range is available for firmware code, so cards could bring drivers in ROM for instant usage. Since the 64 KiB address range of a 6502 doesn't allow big chunks of memory to be set aside - and many drivers won't need more than a few bytes anyway, each slot did get a additional 256 byte address range assigned. This was usually used for driver code and called 'Peripheral Card ROM Space'. Access was signaled by another select line called /IOSEL. So if a card did bring driver code, all it needed was to fit it in a ((E)P)ROM and have address, data and CE line tied to the respective slot lines. Again, no additional hardware needed.

256 bytes may not sound like much, but be assured, it is quite a lot. There where many I/O cards where the whole driver didn't even need that much space. Keep in mind, Woz managed to put the whole boot loader code for the Disk II controller into a 256 bytes - and that includes all necessary drive handling. There is a nice email from Steve Wozniak preserved, where he not only described that he did put a 'whole' monitor program for a pre-Apple 1 prototype into 256 bytes of PROM, but also mentions his 256 byte printer driver for the Apple II.

In case a card needs to offer much more complicated driver code, which couldn't fit in 256 bytes (or a multiple using some switching), another 2 KiB area, now shared by all cards, signaled by /IOSTROBE, was set aside. This sharing was time sliced - meaning that only one card could have its hardware visible at a given time. Therefore a hardware based protocol for activation and deactivation was needed. Whenever a driver needed access to address space, like for more code in a ROM, it first needed to deselect whatever was active before and then map in its own code. To do this all cards watched address $CFFF and deselected their ROM when it got accessed. To enable each card could use whatever private method. Here for the first time hardware beyond the device in question (like ROM) was needed. Still only one flip-flop and a wide AND gate.

All of this together (including all on board resources) resulted in a 4 KiB I/O space within the Apple II memory map. Located at $C000-$CFFF it looked like this:

$C000..C07F On Board Resources
$C080..C08F Slot 0 /DEVSEL area (16 byte register file)
$C090..C09F Slot 1 /DEVSEL area
... repeated for Slot 2..6
$C0F0..C0FF Slot 7 /DEVSEL
$C100..C1FF Slot 1 /IOSEL area (256 bytes 'PROM')
... repeated for Slot 2..6
$C700..C7FF Slot 7 /IOSEL area
$C800..CFFF Common area for all Slots (2 KiB 'ROM')

Now it's also easy to see why slot 0 could only house certain cards, as its 'PROM' area was used for the internal I/O and the register windows of all other slots.

These great preparations for simple I/O card design are for sure part of the success the Apple II had. From a hardware point it was extremely easy to enhance the system.

In addition to the hardware definitions a simple software protocol was added to make use of the driver code from BASIC, as well from assembly. In general PR#n or IN#n did set the output or input vector (CSW/KSW) to $Cn00, thus handing over data to the cards routines or taking it from there. Cards with dual functions did need to manipulate the vectors accordingly. Later on, with the Pascal system, a more sophisticated protocol of card IDs, and multiple entry points got developed. But that's a different story.

  • If I had a particular card then, say a mockingboard, it will be found in various areas in memory depending on which slot I put it in. That's what I didn't understand. So a game will need to detect its location and adjust itself. Feb 12, 2018 at 18:41
  • @Wilson exactly. Scanning 7 locations isn't a big deal, is it? Next to every modern system does it the same way - PCI, VME, you name it. An open architecture needs to be flexible, and the Apple II just did it 20 years before the PC went the same way. Fixed addresses for I/O cards are only a good idea for closed systems.
    – Raffzahn
    Feb 12, 2018 at 19:21
  • 2
    Not an expert on Apple IIs, but I believe in your 3rd paragraph you mean DEVSEL not IOSEL? Also, may be worth pointing out that due to the 6502's address generation architecture, it's easy to write code that's relocatable on 256-byte boundaries, but other sizes could be tricky, therefore the 256-byte limit for the PROMs was probably the only sensible choice.
    – Jules
    Feb 13, 2018 at 10:38
  • 1
    @Jules Youre right. thanks for reading careful and reminding me. | Relative addressing on the 6502 is not tied to page boundries, but limited to a +/-127 distance. So already 256 bytes might be to far for a jump. In ROM there isno way of relocation. I say the sizes are that way, asthey just add up nicely. Also, the 256x8 (and x4) PROMs where common available chips when the II was designed. So it was a perfect match. I added a linkt to a Mail of Wozniak about the PROMs.
    – Raffzahn
    Feb 13, 2018 at 11:36
  • 1
    The advantage is way before any code: No decoding means less complicated and less expensive cards. Not to mention plug and play by cofigurign software instead of hardware. An S100 card had to spend several dollars on power regulation and decoding - and still required a cumbersomehandling of switches - eventually with pulling, reconfiguring and installign the card several times, while on the Apple everything was software driven.
    – Raffzahn
    Sep 1, 2018 at 18:26

I'm not an Apple expert but I think I can answer this just based on the quote and general computer architecture knowledge.

In general, address decoding comes basically down to this: "For a given address, which device should respond?" Each device (typically a chip or an expansion card) has a range of addresses assigned to it; decoding is more about deciding which range an address falls into (and less about identifying which specific address within the range, that is the device's responsibility). So having an address decoder that will identify which-of-several 16-byte ranges, and another that will identify which-of-several 256-byte ranges can be used to select slots (and thereby assign each slot one of those ranges).

The two decoding signals each do something different. One of them tells the card "you have been selected for an I/O access in your 16-byte I/O space", and the other tells the card "you have been selected for a memory access in your 256-byte PROM space". In both cases the card can find out the exact address within the range (and do its own decoding of that) by looking at other pins of the slot (4 pins to get the exact I/O address and 8 pins to get the exact PROM address). If each card had to do its own decoding, each card would need to look at all 16 bits of the 6502 address bus, which is considerably more work than just looking at 4 or 8 bits.

The 256-byte PROM certainly could contain a device driver; that is the most obvious use but it need not be executable or, I suppose, even a ROM. There's no particular reason a card couldn't use it to provide 256 bytes of scratchpad RAM instead. It could even be used as a larger I/O space if for some reason 16 bytes wasn't enough for that.

You must log in to answer this question.

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