The basic steps for Z80 interrupt mode 0 are:
This interrupt mode can't be used reliably on the CPC. The 8-bit interrupt-vector read by the Z80 will normally be &FF, but this is not guaranteed if expansion peripherals are connected. (When &FF is read, the equivalent of a "RST 38H" instruction is executed, and interrupt mode 0 will act the same as interrupt mode 1). Therefore, because the 8-bit interrupt-vector is not always the same it is not advisable to use this interrupt mode on the CPC.
The CPC+ hardware can generate a 8-bit interrupt-vector, and this is normally used with Z80 Interrupt mode 2. The 8-bit interrupt-vector depends on the source of the interrupt (raster interrupt or a interrupt from a DMA channel). The 8-bit interrupt vector is reliable and can be predicted, and as a result the CPC+ can use the Z80 interrupt mode 0.
However, the number of instructions that can be executed in Z80 interrupt mode 0 on the CPC+ is limited, because there is a limited set of numbers that can be defined by the CPC+ 8-bit interrupt vector.
The 8-bit interrupt vector is constructed as follows:
(See "Arnold V" Specification, Issue 1.4, section 2.7 and Extra CPC+ information for more details)
As a result a limited set of instructions can be executed.
I used the following test programs and observed the results:
;; program 1 org &8000 ;; disable interrupts di ;; unlock asic to gain access to asic registers ld b,&bc ld hl,sequence ld e,17 .seq ld a,(hl) out (c),a inc hl dec e jr nz,seq ;; page-in asic registers ld bc,&7fb8 out (c),c ;; set z80 interrupt mode 0 im 0 ;; set 8-bit interrupt vector (bits 7..3). ;; bit 0 = automatically clear interrupts ld a,0 ld (&6805),a ;; page-out asic registers ld bc,&7fa8 out (c),c ;; enable interrupts ei ;; wait for next interrupt halt ;; the halt instruction will wait for a interrupt-request. When this is received, the ;; program counter is advanced to the next instruction before the interrupt is executed. ;; So after "halt" has been executed, the program counter will be pointing ;; to the "LD D,E" instruction. ;; a "dummy" instruction ld d,e di ;; disable interrupts so we can change interrupt mode safely im 1 ;; set Z80 interrupt mode 1 (see comment for "BRK" below) brk ;; this is a special instruction provided by the Maxam assembler/dissassembler/monitor ;; when executed a dump of the register values is displayed. This instruction assembles ;; to a RST 30H. This instruction uses the Amstrad firmware to execute, and the Amstrad ;; firmware requires interrupt mode 1. ;; this is the sequence to unlock the ASIC extra features .sequence defb &ff,&00,&ff,&77,&b3,&51,&a8,&d4,&62,&39,&9c,&46,&2b,&15,&8a,&cd,&ee
In the code above, the only interrupts that will be active will be the standard raster interrupts which occur approximatly every 52 scan-lines (the other interrupt sources must be explicitly enabled and triggered for an interrupt to occur from these sources). When a raster interrupt has been signalled, the ASIC will provide a 8-bit interrupt-vector with bits 2..0 set to 6. In the program I have set the remaining bits to 0, so the complete 8-bit interrupt-vector will be &06, which is the equivalent of the "LD B,n" instruction. ("LD B,n" is a multi-byte opcode consisting of two bytes. The first byte is the opcode, and the second byte is the value of "n". After this instruction has been executed B will have the value defined by "n")
Before this program was executed B has the value &7F, but after this program had executed B had the value &53. &53 is the opcode for the "LD D,E" instruction.
I found that if I changed the "LD D,E" instruction to another instruction, the value of B would always be the same as the first opcode-byte of the replacement instruction. I also found that the instruction following the "HALT" was always executed, therefore the program counter was not incremented when the additional opcode bytes were fetched.
Therefore the second byte of the "LD B,n" instruction is always being loaded from the current program counter location.
Using program 1, I could use any opcode where bits 2..0 are "6".
To use other instructions, I would need to enable another interrupt source:
;; program 2 org &8000 ;; disable interrupts di ;; unlock asic to gain access to asic registers ld b,&bc ld hl,sequence ld e,17 .seq ld a,(hl) out (c),a inc hl dec e jr nz,seq ;; page-in asic registers ld bc,&7fb8 out (c),c ;; wait for end of vsync ;; I use this so I can predict when the next raster interrupt will occur ;; In the CPC and CPC+ design a raster interrupt is triggered on the second HSYNC after the ;; the start of the VSYNC signal. ld b,&f5 .l1 in a,(c) rra jr nc,l1 .l2 in a,(c) rra jr c,l2 ;; set interrupt mode 0 im 0 ld hl,&4030 ;; dma instruction "issue interrupt request and stop executing dma list" ld (&9000),hl ld hl,&9000 ld (&6c04),hl ;; set dma channel 1 address (source of dma instruction list for channel 1) ld a,0 ld (&6c06),a ;; set dma channel 1 prescalar ld a,&20 ld (&6805),a ;; set interrupt vector ;; clear raster counter (has the effect of forcing next raster interrupt to not ;; occur closer than 52-HSYNCs). ld bc,&7f00+%10011100 out (c),c ld a,%00000010 ;; enable dma channel 1, first instruction will execute on the next HSYNC ld (&6c0f),a ;; page-out asic registers ld bc,&7fa0 out (c),c ;; enable interrupts ei ;; wait for interrupt to occur halt ;; "dummy" instructions ld d,h ld e,l di ;; disable interrupts so we can change interrupt mode safely im 1 ;; set Z80 interrupt mode 1 (see comment for "BRK" below) brk ;; this is a special instruction provided by the Maxam assembler/dissassembler/monitor ;; when executed a dump of the register values is displayed. This instruction assembles ;; to a RST 30H. This instruction uses the Amstrad firmware to execute, and the Amstrad ;; firmware requires interrupt mode 1. ;; this is the sequence to unlock the ASIC extra features .sequence defb &ff,&00,&ff,&77,&b3,&51,&a8,&d4,&62,&39,&9c,&46,&2b,&15,&8a,&cd,&ee
This program sets up a simple dma list with a single instruction. The DMA instruction triggers a interrupt and stops the execution of the list. Before the DMA list is executed, the raster counter is reset, this forces the next raster interrupt to occur 52 lines later. As a result the DMA interrupt is serviced before the raster interrupt. DMA channel 1 is used, and bits 2..0 of the 8-bit vector will be set to &02. I have setup bits 7..3 to &20, making the final 8-bit interrupt-vector &22. &22 is the "LD (nnnn),HL" instruction. (The "LD (nnnn),HL" is a three byte opcode. The second and third byte define "nnnn" and is the memory address at which HL is written.).
After this program was executed I found that HL had been written to address &5454. &54 is the opcode for the "LD D,H" instruction. Since bits 7..0 and bits 15..8 of the address were the same, the same byte must have been read twice. This implies that the program counter was not incremented for any of the opcode byte fetches. I also found that DE matched the value of HL, therefore the "LD D,H" and "LD E,L" instructions were executed.
I found that if I changed the "LD D,H" instruction, the address was always generated from the first opcode-byte of the replacement instruction. Therefore the opcode bytes were fetched from the current program counter location.
From these experiments I found the following:
I have not yet tested the following:
As a result of being able to use interrupt mode 0 with the CPC+, I was able to confirm the action of the following unofficial instructions:
Opcode | Action |
---|---|
ED 4E | same as IM 0 |
ED 6E | same as IM 0 |
The following program was used to confirm these instructions:
;; program 3 org &8000 ;; disable interrupts di ;; unlock asic to gain access to asic registers ld b,&bc ld hl,sequence ld e,17 .seq ld a,(hl) out (c),a inc hl dec e jr nz,seq ;; setup for interrupt mode 1 ld a,&c3 ld hl,im1_interrupt_handler ld (&0038),a ld (&0039),hl ;; setup for interrupt mode 2 ;; initialise bits 15..8 of interrupt vector ld a,&40 ld i,a ;; setup interrupt handler jumpblock ld ix,&4000 ld hl,im2_interrupt_handler ld b,0 .sim2 ld (ix+0),l ld (ix+1),h inc ix inc ix djnz sim2 ;; disable interrupts di ;; set interrupt mode ;; {insert instruction here} defb &ed,&4e ;; enable interrupts ei ;; wait for interrupt halt ;; (this is used by interrupt mode 0) nop di ;; disable interrupts so we can change interrupt mode safely im 1 ;; set Z80 interrupt mode 1 (see comment for "BRK" below) brk ;; this is a special instruction provided by the Maxam assembler/dissassembler/monitor ;; when executed a dump of the register values is displayed. This instruction assembles ;; to a RST 30H. This instruction uses the Amstrad firmware to execute, and the Amstrad ;; firmware requires interrupt mode 1. .im1_interrupt_handler ld b,1 ei ret .im2_interrupt_handler ld b,2 ei ret ;; this is the sequence to unlock the ASIC extra features .sequence defb &ff,&00,&ff,&77,&b3,&51,&a8,&d4,&62,&39,&9c,&46,&2b,&15,&8a,&cd,&ee
I tested the above program with the official instructions: "IM 0", "IM 1" and "IM 2".
Therefore from observing the value of B after the program had executed I could determine the function of the instructions.
So, the operation of multi-byte instructions may be different with hardware designs that fully support interrupt mode 0.
Any signals that may be generated by the Z80 to fetch extra opcode bytes will be ignore. As a result, the observations I have seen may only apply to the CPC+ hardware design.