Skip to content

Unstable CCF SCF Behaviour

David Banks edited this page May 26, 2024 · 39 revisions

Contents

Introduction

This page documents the behaviour of the undocumented X/Y flags following a CCF/SCF instruction. The tests were performed on an Acorn Z80 Second Processor, at three different clock speeds: 2.0 MHz, 3.5 MHz and 6.0 MHz. NMOS and CMOS Z80 processors were tested, from Zilog, NEC and SGS/ST.

The test renders a 256x256 pixel graphic that shows the X or Y flag value for all values of F (Y axis) and A (X axis)

For each processor, a total of 16 test cases were generated, varying the following:

  • plotting the X flag (bit 3) and the Y flag (bit 5)
  • testing CCF and SCF
  • testing with four different instruction sequences

The four different instruction sequences were

Sequence A:

POP AF
CCF or SCF
PUSH AF

Sequence B:

POP AF
NOP
CCF or SCF
PUSH AF

Sequence C:

POP AF
LD R,A
CCF or SCF
PUSH AF

Sequence D:

POP AF
XOR &00
CCF or SCF
PUSH AF

Sequences A, B and C cover the case where the preceding instruction doesn't update the flags, and sequence D covers the case where the preceding instruction does update the flags.

Note: Patrik Rak produced similar test in 2022 called z80ccfscr. That test explores a smaller state space:

  • Flags in the range 40-4F
  • CCF only
  • Sequence A only

Results

You can explore the results on the following pages:

Organized by Processor

Organized by Clock Speed

Everything

Conclusions

Stable Behaviours

Four of the processors exhibited stable behaviour, with no random element, and consistent as the clock speed was varied.

Three of these four processors behaved identically:

  • Zilog NMOS with date Code 8039 (the oldest Zilog tested)
  • Zilog CMOS
  • SGS NMOS

We will refer to this behaviour as "pattern A":

if <previous instruction modified the flags> then
   YF = A.5
   XF = A.3
else
   YF = YF | A.5
   XF = XF | A.3
endif

This is the behaviour discovered by Patrik Rak in 2012. It depends on the whether or not the preceding instruction modifies the flags. This is commonly known as the "Q factor".

The fourth processor that was stable was the Zilog NMOS processor with date code 8811. This exhibited a different, much simpler, form of stable behaviour.

We will refer to this behaviour as "pattern B":

YF = A.5
XF = A.3

This is a degenerate form of pattern "A", which doesn't depend on the "Q factor".

Note: This processor is genuine and was the original processor from a Spectrum +2B. The date code matches a photo on Wikipedia link

Unstable Behaviours

Three of the processors exhibited unstable behaviour, where there was some kind of random element:

  • Zilog NMOS with date code 8634
  • ST CMOS
  • NEC NMOS

The Zilog NMOS processor with date code 8634 displayed an unstable form of pattern A:

if <previous instruction modified the flags> then
   YF = A.5
   XF = A.3
else
   YF = (YF & random) | A.5
   XF = (XF & random) | A.3
endif

You can see the effect of the random element in this test result:

The NEC NMOS processor displayed a different form of unstable behaviour:

if <previous instruction modified the flags> then
   YF = A.5
   XF = A.3
else
   YF = (YF | random) & A.5
   XF = (XF | random) & A.3
endif

You can see the effect of the random element in this test result:

What's interesting is the random factor here is clearly clock speed dependent.

At 2.0 MHz:

At 3.5 MHz:

At 6.0 MHz:

This is consistent with the charge on an internal node slowly leaking away.

At low (decreasing) clock speeds the behaviour tends towards pattern "B":

   YF = A.5
   XF = A.3

At high (increasing) clock speeds the behaviour tends towards:

if <previous instruction modified the flags> then
   YF = A.5
   XF = A.3
else
   YF = YF & A.5
   XF = XF & A.3
endif

The ST CMOS processor shows yet another variation: the Y flag is stable and the X flag is unstable:

if <previous instruction modified the flags> then
   YF = A.5
   XF = A.3
else
   YF = YF (i.e. unchanged)
   XF = (XF & random) | A.3
endif

You can see the effect of the random element in this test result:

It's quite likely that if we tested more processors we would discover further variations.

The NEC CMOS processor is the strangest of all, and I haven't yet sat down and tried to analyse it.

Further work

I have some more processors that I plan to test:

  • further Zilog (NMOS and CMOS) with different date codes

Test Program

The above tests were run on a Acorn Z80 Second processor using the following BASIC/assembly language program.

   10 MODE 4
   20 INPUT "CPU NAME ";CPU$
   30 INPUT "DATE CODE ";DATE$
   40 INPUT "CLOCK SPEED ";SPEED$
   50 FOR F=0 TO 1
   60   FOR INSTR=0 TO 3
   70     FOR CF=0 TO 1
   80       code%=&E000
   90       OSWRCH=&FFEE
  100       FOR I%=0 TO 2 STEP 2
  110         P%=code%
  120         [OPT I%
  130         .test
  140         \ SET Y/X FLAGS
  150         LD DE,&0000
  160         .loop
  170         PUSH DE
  180         PUSH DE
  190         POP AF
  200         NOP
  210         NOP
  220         POP AF
  230         ]
  240         IF INSTR=1 THEN [OPT I%:NOP:]
  250         IF INSTR=2 THEN [OPT I%:LD R,A:]
  260         IF INSTR=3 THEN [OPT I%:XOR &00:]
  270         IF CF=0 THEN [OPT I%:CCF:]
  280         IF CF=1 THEN [OPT I%:SCF:]
  290         [OPT I%
  300         \ SAVE FLAGS
  310         PUSH AF
  320         POP HL
  330         LD A,L
  340         ]
  350         IF F=1 THEN [OPT I%:RRCA:RRCA:]
  360         [OPT I%
  370         AND &08
  380         JR NZ skip
  390         LD A,25
  400         CALL OSWRCH
  410         LD A,71
  420         CALL OSWRCH
  430         LD L,D
  440         LD H,&00
  450         ADD HL,HL
  460         ADD HL,HL
  470         LD A,L
  480         CALL OSWRCH
  490         LD A,H
  500         CALL OSWRCH
  510         LD L,E
  520         LD H,&00
  530         ADD HL,HL
  540         ADD HL,HL
  550         LD A,L
  560         CALL OSWRCH
  570         LD A,H
  580         CALL OSWRCH
  590         .skip
  600         INC E
  610         JR NZ,loop
  620         INC D
  630         JR NZ,loop
  640         RET
  650         ]
  660         NEXT
  670       MODE 4
  680       MOVE 0,0
  690       MOVE 1023,0
  700       PLOT 85,0,1023
  710       PLOT 85,1023,1023
  720       VDU 23,1,0;0;0;0;
  730       MOVE 1100,250
  740       DRAW 1100,100
  750       DRAW 1250,100
  760       MOVE 1100,250:DRAW 1080,230
  770       MOVE 1100,250:DRAW 1120,230
  780       MOVE 1250,100:DRAW 1230,120
  790       MOVE 1250,100:DRAW 1230,80
  800       VDU 31,33,26,ASC"F"
  810       VDU 31,36,29,ASC"A"
  820       VDU 28,32,31,39,0,30
  830       PRINT "CPU:"'CPU$
  840       VDU 31,0,3
  850       PRINT "DATE:"'DATE$'
  860       PRINT "CLOCK:"'SPEED$'
  870       PRINT "TESTING:";
  880       IF INSTR=0 PRINT "POP AF"
  890       IF INSTR=1 PRINT "POP AF"'"NOP"
  900       IF INSTR=2 PRINT "POP AF"'"LD R,A"
  910       IF INSTR=3 PRINT "POP AF"'"XOR &00"
  920       IF CF=0 PRINT "CCF" ELSE PRINT "SCF"
  930       PRINT "PUSH AF"'
  940       PRINT "GRAPH:"'CHR$(ASC("X")+F);" FLAG"'
  950       PRINT "X AXIS:"'"A:00-FF"'
  960       PRINT "Y AXIS:"'"F:00-FF"'
  970       CALL test
  980       REM TAKE A SCREEN SHOT
  990       *MOTOR 1
 1000       T=TIME
 1010       REPEAT UNTIL TIME>T+20
 1020       *MOTOR 0
 1030       TIME=T
 1040       REPEAT UNTIL TIME>T+100
 1050       NEXT
 1060     NEXT
 1070   NEXT