Understood. I thought maybe EOI had been sent already.
Well this is very interesting. I can only think that for some reason, the subroutine is called from the main execution loop on real hardware. It clearly can't work when called from the timer ISR.
I notice that on both DOSBox and SoftMPU, the first song plays slightly too slowly. However, the length of time that elapses before the song fades out is fixed. As a result the song fades out before it is finished, whereas on real hardware it plays until the end.
Could this code path we're seeing (end of song subroutine called from timer ISR) be a result of this? If we tweak the interrupt generation timer so that the song plays through to the end before fade, would the end of song subroutine be called from main execution loop?
Just throwing ideas out there...
EDIT: Decreasing MPU401_TIMECONSTANT in mpu401.cpp will increase song tempo so that the whole song plays before fade.