6

The 8080 is referred to as an 8-bit CPU because it has an 8-bit data bus, but there are a number of cases where it must perform 16-bit memory access, for example when reading or writing a 16-bit register pair, or the 16-bit program counter when performing subroutine call or return.

I assume it supports unaligned access, i.e. the address is not required to be even.

Does it support fully unaligned access in all cases, i.e. no requirement that both bytes be in the same page? For example, if you try to write a 16-bit register pair to address $7fff, will the second byte be written to $8000? Or if the stack pointer was set to $8001 and you perform a subroutine call, will the return address be written to the addresses $8000 and $7fff?

1
  • 1
    The 8080/Z80 has no concept of an unaligned access - It simply accesses the byte, and, if needed the following one. What you seem to be referring to is a 6502 quirk.
    – tofro
    Dec 5, 2022 at 13:12

3 Answers 3

18

TL;DR:

The 8080 does not know about any alignment. All access is byte access and linear. There is also no page logic. All addresses are handled as straight 16-bit and all data access is 8-bit.

(For additional musings see further down)


In Detail:

I assume it supports unaligned access, i.e. the address is not required to be even.

The 8080 does not handle or even know anything about alignment. A 16-bit value is simply stored at two consecutive (8 bit) memory locations.

Does it support fully unaligned access in all cases,

Yes, 16-bit load/stores can be done from any address.

no requirement that both bytes be in the same page?

No, the 8080 does not have a concept of 'pages'. All address pointers are 16-bit and increments are handled as such.

For example, if you try to write a 16-bit register pair to address $7fff, will the second byte be written to $8000?

Yes (*1).

Or if the stack pointer was set to $8001 and you perform a subroutine call, will the return address be written to the addresses $8000 and $7fff?

Exactly.


The 8080 as 16 Bit CPU

Yes, in some sense the 8080 (and its offspring 8085 and Z80) are 16-bit CPUs - at least when it comes to the register set and address handling. The register file is organized as

  • a set of six 16-bit registers: BC, DE, HL, SP, IP and WZ (*2)
  • directly connected to a 16-bit incrementer/decrementer
  • directly connected to a 16-bit address latch
  • which in turn is connected to the address bus.

Thus each of these registers can, within a single machine cycle be

  • latched to be put on the (external) address bus, and/or
  • incremented or
  • decremented

Note that there is no restriction by implied 'pages' or need for even/odd alignment, as address generation is plain 16 bit with a byte sized memory interface.

The 8080's internal structure is, regarding registers and addressing, fully 16-bit, whereas the 6502 is 8-bit in this respect, operating with separate 8-bit units to handle pages and addresses within pages. This pure 8-bit orientation of the 6502 made it lower cost, but added a few quirks, like dead cycles in case of 8 bit carry in (some) address calculations.


Background

The question may stem from a mix-up between data size requirements and data bus size. Alignment issues can only come up with designs that have a finer address granularity than the external data bus and accessing a data item sized of multiple address units, i.e. a CPU with byte addressing but a multiple byte wide data bus accessing data wider than a single byte - like a 68020 (byte addressing, 4 byte wide data bus) accessing a 16 bit word.

The 8080 does not fall into this category. Addressable data unit and access width do not differ.


*1 - Or more correct it will be address 7FFFh and 8000h :)) SCNR

*2 - Note that AF is not one of them: A is a separate register, while F doesn't exist.

4
  • 1
    The question may also stem from other 8-bit CPUs (eg 6502) that did have some restrictions on accesses that span a (256-byte) page, due either to specific restrictions (zero page indexed addressing, stack access) or bugs (indirect jump bug).
    – psmears
    Nov 9, 2020 at 15:21
  • @psmears Yes, that's exactly what it stems from.
    – rwallace
    Nov 10, 2020 at 19:14
  • Where does the WZ register come from? It wasn't in the 8080 programming I learned in the 1980s, nor is it in present-day Wikipedia's description. Dec 10, 2022 at 23:35
  • @JohnDallman It's an internal register. Ever wondered where an 8080 holds the address given in LDA or STA until that byte is accessed? Or were the address the address to fetch a vectored interrupt is build and held? Exactly, WZ is the internal register pair for all address handling not provided by any user visible pair. You may want to take a look at this answer. They are usually not shown as they are not part of the (user visible) programming model (ISA).
    – Raffzahn
    Dec 11, 2022 at 0:28
6

Although the 8080 and Z80 don't care about alignment, a compiler that targets the 8080 could benefit somewhat from knowing that pointers to 16-bit and 32-bit objects will be aligned. For example, consider the code necessary to process *p += 1; if p is an int*. If p is known to be aligned, a compiler could generate [Z80 opcodes shown]

    ld hl,(p)
    inc (hl)
    jnz noHighByte
    inc l
    inc (hl)
noHighByte:

If a compiler didn't know that p will be 16-bit aligned, it would need to generate an "inc hl" instruction instead of "inc l", which would take an extra two cycles to execute. The extra cost is small enough that none of the 8080/Z80 compilers I've seen made any effort to avoid it, but if a language required that objects be aligned in that fashion it could have offered some benefits.

Interestingly, the benefits would be even larger if small arrays could be guaranteed not to straddle page boundaries, but languages don't offer ways of specifying such constraints. If, e.g., one has a pointer to a char[16]; which is known to be 16-byte aligned, evaluation of p[i]++ could be:

    ld hl,(p)
    ld a,(i)
    add a,l
    ld  l,a
    inc (hl)

instead of something like either:

    ld hl,(p)
    ld a,(i)
    ld e,a
    ld d,0
    add de,hl
    inc (hl)

or

    ld hl,(p)
    ld a,(i)
    add a,l
    ld  l,a
    jnc noCarry
    inc h
noCarry:
    inc (hl)

with the latter version being bigger, but leaving DE unaffected. Note that the last version is bigger than the middle one, but faster, since "ADD HL,DE" is a very slow instruction.

In order to exploit things like the latter speed up, a compiler would have to know that indexing p by i wouldn't cross a page boundary, and no languages I know of would generally provide any way of telling a compiler that.

-1

The original was firmly 8-bit data bus, and the only benefit of aligned pointers was that you could reuse the low bit or two for tags of some sort. Alignment made no difference, there was no concept of "unaligned" access from the hardware perspective. It just didn't matter at all.

To get an architecture like 8080/Z80 to benefit from a 16-bit bus, it needs to support unaligned access at full speed, since all the code is written assuming that unaligned access is not penalized. That's why 8080/Z80 only had an 8-bit data bus: it would take lots of expensive hardware and pins to do otherwise.

I'm in the middle of implementing a 16-bit data bus homebrew Z80. It is somewhat harder than it might look like at first :)

The only way to have full-speed unaligned 16-bit access is to split the address space into odd and even bytes, and have a dedicated address bus for each, with an incrementer for the even byte on unaligned word accesses. I.e. the odd bus is read at address A+0, and the even bus is read at address A+1.

In times of Z80, 16-bit wide fast adders were prohibitively expensive in die area, to the extent that Z80 had a 4-bit wide ALU. The fastest wide adder would have been probably dense Kogge-Stone adder back then, and that takes over 20x as much die area as a 4-bit ripple adder, and the layout is daunting to say the least.

To get 16-bit bus to work well, you also need at least a prefetch buffer to efficiently deal with variable-length opcodes - similar to what 8086 had to do. That buffer has to be refilled word at a time, but the the word fetch method must support unaligned word access to have minimum possible latency and reasonable cycle count determinism independently of where in the address space an instruction is located.

The stack can also be unaligned, even though in most cases SP was word aligned, though it didn't have to be. Recall that on 8080/Z80, all stack operations are word-wide, but were executed byte-wide on the bus. Nothing inside Z80 had to increment addresses by 2 :)

You must log in to answer this question.

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