@@ -35,6 +35,7 @@ pub const Z80 = struct {
3535
3636 // Q latch used for undocumented flag behavior (SCF/CCF).
3737 q : u8 = 0 ,
38+ q_needs_update : bool = true ,
3839
3940 // "WZ" register.
4041 // https://www.grimware.org/lib/exe/fetch.php/documentations/devices/z80/z80.memptr.eng.txt
@@ -125,6 +126,7 @@ pub const Z80 = struct {
125126 self .r = 0 ;
126127 self .im = 0 ;
127128 self .q = 0 ;
129+ self .q_needs_update = true ;
128130 self .iff1 = false ;
129131 self .iff2 = false ;
130132 self .cycles = 0 ;
@@ -1437,16 +1439,22 @@ pub const Z80 = struct {
14371439
14381440 // Execute a single instruction
14391441 pub fn execute (self : * Z80 ) void {
1440- // Snapshot flags before executing an instruction to approximate Q latch
1441- // semantics: instructions that change F copy new F to Q, others clear Q.
1442+ // Snapshot flags before executing the instruction so we can approximate
1443+ // Q latch semantics: instructions that change F copy new F to Q,
1444+ // instructions that don't change F leave Q unchanged, and some
1445+ // instructions (EX AF,AF', POP AF, etc.) explicitly clear Q.
14421446 const old_f : u8 = self .getF ();
14431447 // std.debug.print("opcode = 0x{X:0>2}\n", .{self.peekByte()});
14441448 self .executeOpcode (self .fetchOpcode ());
1445- const new_f : u8 = self .getF ();
1446- if (new_f != old_f ) {
1447- self .q = new_f ;
1449+ if (self .q_needs_update ) {
1450+ const new_f : u8 = self .getF ();
1451+ if (new_f != old_f ) {
1452+ self .q = new_f ;
1453+ }
1454+ // If F is unchanged, Q stays as-is.
14481455 } else {
1449- self .q = 0 ;
1456+ // Instruction explicitly managed Q; re-enable automatic updates.
1457+ self .q_needs_update = true ;
14501458 }
14511459 }
14521460
@@ -1508,6 +1516,9 @@ pub const Z80 = struct {
15081516 const tmp = self .af ;
15091517 self .af = self .af_ ;
15101518 self .af_ = tmp ;
1519+ // EX AF,AF' does not affect Q latch.
1520+ self .q = 0 ;
1521+ self .q_needs_update = false ;
15111522 self .cycles += 4 ;
15121523 },
15131524 0x09 = > {
@@ -2559,6 +2570,9 @@ pub const Z80 = struct {
25592570 self .incSP ();
25602571 self .setB (self .memory [self .sp ]);
25612572 self .incSP ();
2573+ // POP BC does not affect Q latch.
2574+ self .q = 0 ;
2575+ self .q_needs_update = false ;
25622576 self .cycles += 10 ;
25632577 },
25642578 0xC2 = > {
@@ -2710,6 +2724,9 @@ pub const Z80 = struct {
27102724 self .incSP ();
27112725 self .setD (self .memory [self .sp ]);
27122726 self .incSP ();
2727+ // POP DE does not affect Q latch.
2728+ self .q = 0 ;
2729+ self .q_needs_update = false ;
27132730 self .cycles += 10 ;
27142731 },
27152732 0xD2 = > {
@@ -2859,6 +2876,9 @@ pub const Z80 = struct {
28592876 self .incSP ();
28602877 self .setH (self .memory [self .sp ]);
28612878 self .incSP ();
2879+ // POP HL does not affect Q latch.
2880+ self .q = 0 ;
2881+ self .q_needs_update = false ;
28622882 self .cycles += 10 ;
28632883 },
28642884 0xE2 = > {
@@ -2984,6 +3004,9 @@ pub const Z80 = struct {
29843004 self .incSP ();
29853005 self .setA (self .memory [self .sp ]);
29863006 self .incSP ();
3007+ // POP AF does not affect Q latch.
3008+ self .q = 0 ;
3009+ self .q_needs_update = false ;
29873010 self .cycles += 10 ;
29883011 },
29893012 0xF2 = > {
@@ -3574,6 +3597,9 @@ pub const Z80 = struct {
35743597 const nn = self .fetchWord ();
35753598 self .sp = self .readWord (nn );
35763599 self .wz = nn +% 1 ;
3600+ // LD SP,(nn) does not affect flags; ensure Q is cleared.
3601+ self .q = 0 ;
3602+ self .q_needs_update = false ;
35773603 self .cycles += 20 ;
35783604 },
35793605
0 commit comments