25

Reading through the Z80 datasheet, I noticed something interesting. The Z80 separates its instruction execution into separate phases (which are called "machine cycles" in the official literature, but I don't like that terminology because it is too easy to confuse with clock cycles ... a machine cycle consists of multiple clock cycles, usually 3 or 4) that perform different kinds of operation. In the first such phase, which is called M1, the processor fetches the instruction to execute in the remaining phases, then once execution has finished it cycles back to M1 again and fetches the next instruction.

Interestingly, the processor has a pin that provides a signal identifying whether or not it is in the M1 phase. Apparently this is because the memory timing for instruction fetch is different to memory accesses that are performed by the executing instruction, so if your RAM has an access time > 1.5 clock cycles but < 2 cycles you need to assert the /WAIT signal during instruction fetches but not data fetches. But I see no reason this couldn't be used as a seventeenth address line, effectively allowing the Z80 to address an entire 64K of data memory without the inconvenience of needing to fit its program in the same space.

Are there any examples of machines that did this? Or any reason why it wouldn't work?

25
  • 2
    As it is already answered, direct way of using /M1 as additional address line is impossible. However, you can use external hardware to trace Z80's opcode flow and direct accesses to different memory areas depending on whether Z80 still reads command operands (for example LD HL,#1234 has singe M1 cycle and then two ordinary read cycles to read operand) or make further accesses commanded by the instruction just read. Unfortunately, the complexity of such a hardware is relatively high and it could be easily made only in FPGA or CPLD devices.
    – lvd
    Jul 31, 2017 at 15:44
  • 1
    CP/M Plus supported bank-switching architectures. One concept I remember was to have the Z80 address space divided into four 16KB logical banks, and have these mapped freely to 16KB-segments of a 1MB physical address space. A single application could still only use ~56K, but the O/S resided in its own bank. I used that for a memory-mapped color graphics card with 512 by 768 RGB pixels, occupying 384 KB of physical space and addressed by some tricky bank switching ASM code. Aug 4, 2017 at 15:10
  • 1
    @lvd: Interesting. I'm curious what the creators of the Z80 were thinking with the whole IX/IY design (intended usage patterns, etc.) If there had been plans for some instructions that got dropped (e.g. more 16-bit transfers to/from IX/IY) the design might make sense, but doing anything with the registers always seems unbearably awkward. A prefix to compute either "BC+n", "DE+n", "HL+n" or "SP+n" with a displacement +/-31 and use that in place of "(HL)" or "(SP)" in the next instruction would seem like it could have been more useful than all of the IX/IY functionality put together.
    – supercat
    Jun 1, 2018 at 15:47
  • 1
    @lvd: I'm sure the designers of the Z80 were smart people, but IX/IY are so awkward to use it seems like there must be something "missing" from the intended design. Being able to use short displacements is sometimes useful, but the required six-byte 29-cycle ld ix,0 / add ix,de sequence to transfer a value into ix can add a displacement "for free", meaning the short displacements add cost but little value). If, however, there had been a two-byte 8-cycle ex de,ix/iy instruction, the IX/IY displacements could have been much more valuable. Maybe DD EB and FD EB had been planned?
    – supercat
    Jun 1, 2018 at 16:18
  • 1
    @supercat ex de, i[xy] would have been tricky to implement: ex de, hl just toggles a flipflop that inverts the select lines to the de and hl registers. Extending that to IX and IY would have required either a much more sophisticated decoder or actual data swapping, which with only a 4-bit wide data path into or out of temporary storage would have taken at least 16 cycles.
    – Jules
    Jun 1, 2018 at 19:31

8 Answers 8

25

The M1 line literally means machine cycle 1 — and you're right about timing; the instruction fetch part of an M1 cycle is only two clock cycles long and will sample the data lines in the first clock cycle that it finds WAIT not asserted in, whereas a normal read is three clock cycles long and will sample the data lines in the clock cycle immediately after the first in which it finds WAIT not asserted.

However when the Z80 reads an operation with an operand, such as LD A, 23, only the opcode itself is accessed in machine cycle 1. So if you were to use M1 naively as a seventeenth address line then you'd see that operands were no longer fetched from the same area as operations. Which would be problematic for most execution flows.

However, what you could do is latch some part of the address fetched in each M1 cycle and use that to alter the memory map — so that code in one address range sees one memory map but code in another range sees a different memory map. This idea is lifted from an expansion of the Acorn Electron, which is a 6502 machine but the SYNC pin there does pretty much the same thing in indicating which bytes are opcodes. This particular expansion switches the RAM seen when the OS is in its drawing routines with a separate bank visible to the BASIC ROM. It thereby takes the area used for display out of the normal addressing range.

12

In the 80s and 90s some arcade games the M1 signal was used for anti-bootlegging measures. The developers used use a customized assembler that would allow him/her to mark code and data sections, and the assembled program was encrypted such that code sections were encrypted one way and data sections were encrypted another way. The resulting combination of encrypted code and data would be written to a single EPROM.

The arcade game hardware had a custom module containing a Z80 and support circuitry to execute the game program by applying one of two different decryption functions based on the state of the M1 pin. In theory this made it very difficult to duplicate a program, and to modify it (often copyright messages were removed by bootleggers).

In practice bootleggers would duplicate games by dumping the Z80 address space twice after gaining physical access to the Z80 data/address/control bus in the module, once for each state of the M1 pin. This produced two copies of the EPROM contents, one with the content decrypted as if it were code, another with the content decrypted as if it were data.

The bootleg arcade game would have a regular Z80 and now two EPROMs and it would select which EPROM was being used based on M1 to pick the decrypted program or data. This essentially reversed the protection the original developers applied by de-interleaving the interleaved encrypted code and data.

A more dedicated effort was to manually merge the decrypted code and decrypted data back together into a single unified EPROM, but this was time consuming and error prone and was not commonly done, if at all, until modern times when arcade collectors wanted to replace the short-lived custom modules with a plain Z80.

So M1 has some useful, if obscure, functionality.

1
  • 1
    If a machine used a CD4052 (4x2 MOSFET switch) with AB inputs connected to M1 and A1, and the other pins connected between the Z80's D0/D1 and the rest of the bus so that the pins would be swapped only on M1 cycles from even addresses, code could guard against the EPROM-clone trick by copying code to RAM and running it there (sometimes changing the code's address mod 4). Not sure if anyone ever took things that far, but I think it would offer a pretty good level of reverse-engineering protection for the cost of a single chip.
    – supercat
    Jun 1, 2018 at 16:51
8

In the early '80s I worked on a "communications processor" which let you use ASCII terminals as an IBM 3270 display subsystem. The Z80 was critically short of both RAM and program memory (27256 EEPROM chips, IIRC) for the task of emulating 8 displays.

To handle the RAM shortage the hardware got a 8kB bank setup in high RAM that was manually switched by the program. To handle the program space shortage, there was a "shadow PC counter" that worked with the M1 and a handful of gates to detect the conditions in @Tommy's answer so that program accesses went to the EEPROM chips and true data access went to RAM. There were a handful of IX and IY instructions that we weren't allowed to use.

Ugly, but it allowed the product to handle twice as many connections, which made it an economic winner.

4

But I see no reason this couldn't be used as a seventeenth address line, effectively allowing the Z80 to address an entire 64K of data memory without the inconvenience of needing to fit its program in the same space.

The question as it applies to the Z80 has already been answered, but the more general answer is yes. The obvious example is the 8086. From the perspective of the programmer, addresses are 16 bits wide. However, the chip itself has 20 bits of address space. The extra four bits come from a segment register. Code access is done through the code segment, data accesses through the data segment or the extra segment and stack accesses are done through the stack segment. Thus, the programmer can access up to 256 kilobytes at a time.

Taking the concept of keeping instructions and data separate to the maximum, you can have, not only a separate address space, but separate data paths too. i.e. when you fetch an instruction, it comes in on a different bus to the data. This is known as the Harvard Architecture. The Harvard architecture has the advantage over the Von Neuman architecture in that you can be doing instruction fetches and data accesses at the same time. You can also design caching policies to optimise for instructions and data separately. The disadvantage is having more bus lines.

Internally, almost all modern CPUs use the Harvard Architecture. Instructions are fetched from a different cache to data and the fetch uses a different pathway. So, from the microcoder's point of view, your separation scheme is pretty much standard.

3

Another important function of M1 which isn't yet mentioned is to synchronize in-circuit emulators. An in-circuit emulator is a device which contains a processor, a connector that plugs into a processor socket, and some circuitry between them. The emulator can be set to behave like a processor, but it can at any point or when a certain breakpoint is triggered, disconnect its bus from that of the host system, save the state of registers, and then start executing its own code to provide a user interface with its own buttons and display. That interface may allow individual memory reads and writes to be performed on the host system, and may also allow for operations to copy or fill a range of memory, scan a range of memory for a certain value, configure breakpoints, etc.

While some low-cost debuggers may use interrupts to gain control, that could cause difficulties if a "real" interrupt happens at the same time as the debugger tries to gain control. A better ICE will do do something like wait for the start of an instruction (perhaps figuring that a /M1 cycle which follows a write must be the start of an instruction), disconnect from the host bus and connect to its internal bus, replace the data from the bus with 0xFF (a RST 38h instruction), pretend that the upper address bits on the next two write cycles are all set (so the writes will go somewhere in the range 0xFFF0 to 0xFFFF regardless of the value of SP), and then start executing code from its own ROM.

A proper "start of instruction" indication would have been more convenient than the Z80's "some kind of opcode byte", but most code won't go very long without a write cycle, and once a write cycle occurs the state machine in the ICE should be able to follow things after that. Perhaps an ICE could track things even without M1, but it would require a more complicated state machine that understands almost all Z80 opcodes rather than just a few prefix bytes.

1
  • Interesting, yes, I'd not considered that use. And of course all of this must have been much simpler on the 8080 (where the M1 signal originated) due to the fact that it didn't have the system of opcode prefixes used by the Z80.
    – Jules
    May 31, 2018 at 0:00
2

As indicated in other answers, M1 can't be used to implement a Harvard addressing scheme, as immediate operands are fetched from program memory in cycles subsequent to M1. But it can be used by hardware to detect parts of program execution.

One use for this was on the ZX Spectrum's Interface One (for Microdrives and serial ports): it detects execution of I/O operations (using M1 + address), and in response, asserts #ROMCS to disable the built-in ROM and enables its own Shadow ROM in the bottom 16K¹. I can't remember how the return to the Spectrum ROM is achieved - it may be the same mechanism, but I no longer have my copy of Spectrum Shadow ROM Disassembly with which to check.


¹ IIRC, an 8K ROM that appears twice in the address space - A13 not decoded.

2

As answered the M1 is not used during full instruction fetch, just the first opcode byte. Note that the M1 has other purposes and shouldn't be messed with, especially if Z80 peripheral chips such as PIO/SIO/CTC etc. are used on same system. Mode 2 interrupts used the M1 along with IORQ as part of interrupt acknowledge, vector placement on bus and end of interrupt processing (peripheral chips detecting RETI ended interrupt). A very sophisticated and powerful interrupt system with interrupt chaining, vectored interrupts etc. (way ahead of the Intel 8086 system and didn't require an external interrupt controller). The business I was in in the eighties built process controllers and communication controllers and we stuck with the Z80 for years because of its peripheral chips and interrupt system.

1

M1 can be used with a helper state machine driven by the opcode contents to determine which accesses are code space and which are data space. Given the Z80 opcode map, such a state machine isn't terribly complex. There are big chunks of opcode space that are quite regular.

For example, the first opcode byte decodes as follows:

(all opcodes octal!)
1 byte: all except given below
2 bytes:
  020,030,...,070 - relative jumps
  0_6             - LD immediate
  3_6             - immediate arithmetic
  323,333         - IN/OUT (m):A
  313             - CB prefix - bit opcodes
  355 then ___    - ED prefix - all except those that are 3 or 4 bytes below
3 bytes:
  001,021,041,061 - LD ww,mn
  042,052,...,072 - LD (mn):HL/A
  3_2             - JP f, mn      
  303             - JP mn
  3_4             - CALL f,mn
  315             - CALL mn
  355 then 0_0    - Z180 IN0
  355 then 0_1    - Z180 OUT0
  355 then 244,264 - Z180 TST m
4 bytes:
  355 then 2_3    - LD (mn):ww
state changes:
  335,375         - IX/IY prefix, don't count this byte, add/override state transitions per IX/IY prefix

IX/IY prefix modifies the expected opcode lengths as follows. Start with the table above, and add/override the following entries:

IX/IY + 1 byte:
  166             - HALT
IX/IY + 2 bytes:
  064,065         - INC/DEC (HL)
  160,261,...,265 - LD (HL), s
  1_6             - LD g, (HL)
  2_6             - arithmetic with (HL) as source
  351             - JP (HL)
IX/IY + 3 bytes:
  066             - LD (HL), m
IX/IY + 4 bytes:
  313 then __6    - CB instructions using (HL)
  355 then 064    - Z180 TST (HL)

At some point in the past I've put together a state machine for the above with a Johnson counter for the 1st, 2nd, etc. byte, steered by discrete logic that decoded all the cases. It's doable, if tedious. Trivial in a PLD or FPGA though.

You must log in to answer this question.

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