kosarev / z80

Fast and flexible Z80/i8080 emulator with C++ and Python APIs
MIT License
63 stars 10 forks source link

Missing undocumented RETI behaviour #28

Closed simonowen closed 3 years ago

simonowen commented 3 years ago

As detailed in the Undocumented Z80, the RETI instruction copies iff2 to iff1, which is the same behaviour as RETN. This isn't mentioned in the official documentation.

I've just tested it on real hardware and can confirm it does restore iff1 from iff2. My test code was as follows:

8000          JR  8000
8002          HALT
8003          DI
8004          LD  A,04
8006          OUT (FE),A
8008          HALT

0038          LD  B,06
003A          DJNZ 003A
003C          EI
003D          RET

0066          LD  HL,8002
0069          PUSH HL
006A          LD  A,02
006C          OUT (FE),A
006E          RETI

It starts with a tight loop with interrupts enabled. The maskable interrupt handler delays long enough for the interrupt to become inactive, then returns. Triggering an NMI sets the border red and uses RETI to restore iff1 and return to a HALT. If interrupts are disabled at this point the border remains red and the CPU is halted. If iff1 is restored to the previous enabled state the HALT waits for the next frame interrupt then continues to set the border green, before halting.

Tests show that both RETI and RETN give the same behaviour on real hardware, with the border turning green. In the emulator RETN works as expected but the border remains red with RETI because iff1 wasn't restored.

The on_reti handler could probably just call on_retn instead of on_return. Though it uses handlers that mention 'retn' (such as set_iff1_on_retn) so it might be better to have separate 'reti' versions?

kosarev commented 3 years ago

Resolved via #32.