Here is a comparison in my source tree of good and bad timer interrupt control logic: https://github.com/cr1901/vbdemo/compare/bad_timer…good_timer
What my code actually does is irrelevant. The important part is that the “demo” resets on button press after reaching the second screen, by jumping unconditionally to the reset vector. However, in the bad_timer tag, the demo will crash upon a jump to reset, presumably because the timer interrupt fires (the crash does not happen if the timer interrupt frequency is sufficiently low). After thinking about this, I’m not certain why this is the case.
HorvatM suggested that I disable interrupts from reaching the CPU, which I did with an asm intrinsic:
asm("SEI;")
For whatever reason (I haven’t looked at the ASM listings), this had no effect- VB and Mednafen still crashed. Turns out, the way to prevent the crash is to disable interrupts/the timer in the Timer Control Register:
HW_REGS[TCR] &= ~(TIMER_ENB | TIMER_INT);
Does anyone have any idea why the SEI instruction did not work? Does the stub crt0 re-enable interrupts, because I somehow doubt there’s anything “magic” about unconditionally jumping to the reset vector location.
Finally, is the following line in the Sacred Tech Scroll relevant?:
When PSW is initialized, the NP flag is set. This must be reset manually using the LDSR instruction. Otherwise, any exceptions or interrupts that occur will result in an immediate deadlock.
I can understand that being true after power on reset. It’s because the vector that will be jumped to differs if an interrupt occurs during NMI interrupt processing (NP flag set), correct?
But since in my code I jump to the reset vector, and it’s not called through external means (Is that even possible on VB?), the registers should keep their previous states, as opposed to reverting to their power-on reset states.
Since I don’t actually trigger an interrupt, the CPU shouldn’t do any housekeeping associated with entering an ISR (especially the reset vector), and the PSW shouldn’t have NP set. Again, I don’t see anything that suggests the unconditional jump ASM code causes the CPU to internally modify its system register state:
asm(".globl _jump_addr"); asm("_jump_addr:"); asm("add -4,sp;"); asm("st.w lp,0[sp];"); asm("jal __dojump;"); asm("ld.w 0[sp],lp;"); asm("add 4,sp;"); asm("jmp [lp];"); asm("__dojump:"); asm("jmp [r6];");
Consequently, I don’t see why the timer interrupt can’t run during an unconditional jump to the reset code. Again, could it be possible that some of the startup code in the crt0 stub that is supplied with gccVB is not interrupt-safe?
HorvatM suggested that I disable interrupts from reaching the CPU, which I did with an asm intrinsic:
asm("SEI;")
But sei enables interrupts. cli disables them.
Maybe you should go a bit more into detail what actually happens. Could it be that the VB doesn’t freeze, but you get stuck in the exception handler or something along those lines?
cYa,
Tauwasser
cr1901 wrote:
Does the stub crt0 re-enable interrupts, because I somehow doubt there’s anything “magic” about unconditionally jumping to the reset vector location.
This would be my guess. If the crt0 has an implicit [font=Courier New]CLI[/font] in there, then interrupts will be enabled even if you bite your tongue really hard.
cr1901 wrote:
Finally, is the following line in the Sacred Tech Scroll relevant?:When PSW is initialized, the NP flag is set. This must be reset manually using the LDSR instruction. Otherwise, any exceptions or interrupts that occur will result in an immediate deadlock.
No, that was me making an error. A reexamination of the V810 documents revealed that interrupts are implicitly ignored during any exception processing: that is, if [font=Courier New]NP[/font] or [font=Courier New]EP[/font] is set, not just [font=Courier New]ID[/font]. If any other exception occurs, like zero division or an invalid opcode, then a deadlock will still take place.
I have a new draft of the document in the works, and in it, this particular mishap has already been corrected.
Tauwasser wrote:
But sei enables interrupts. cli disables them.
Other way around. The bit being set in the status register is called [font=Courier New]ID[/font], standing for Interrupt Disable. When the bit is set, not cleared, interrupts will be ignored.
Guy Perfect wrote:
Other way around. The bit being set in the status register is called [font=Courier New]ID[/font], standing for Interrupt Disable. When the bit is set, not cleared, interrupts will be ignored.
So they use the same mnemonic as all the other platforms out there and make it mean the opposite of what it does on every other platform? That’s fucking brilliant…
cYa,
Tauwasser
Tauwasser wrote:
So they use the same mnemonic as all the other platforms out there and make it mean the opposite of what it does on every other platform?
Let’s take a look:
NES – 2A03, based on the MOS Technology 6502 – Status register has interrupt disable flag. Instructions [font=Courier New]SEI[/font] and [font=Courier New]CLI[/font] are present, which disable and enable interrupts, respectively.
SNES – 65C816, based on the Ricoh 5A22 – Status register has interrupt disable flag, [font=Courier New]SEI[/font] and [font=Courier New]CLI[/font], just like NES. In fact, most of the instructions are binary-compatible.
Game Boy – Unnamed CPU, based on the Sharp LR35902, itself based on the Zilog Z80 – No on-chip interrupt support, but the system contains an I/O register at bus address 0xFFFF, in which individual bits do need to be set to enable interrupts. There is a master interrupt control register, and it needs to be cleared to disable all interrupts.
Game Boy Advance – Stock ARM ARM7TDMI – No on-chip interrupt support, but an I/O register is present here too, requiring bits to be set to enable interrupts. However, master interrupt control can be set to disable all interrupts.
Nintendo DS – Stock ARM ARM7TDMI and ARM946E-S – Similar to GB and GBA, with I/O register where bits need to be set. Likewise, the master interrupt control can be set to disable all interrupts.
*35 minutes of frustrated web searching later*
It occurs to me that the world needs an N64 Sacred Tech Scroll…
Nintendo 64 – Stock NEC VR4300, based on the MIPS Technologies R4300i – Status register (which I learned about in a PDF document and cannot provide a link to) contains an interrupt enable flag, which must be cleared to disable interrupts.
Umm, thanks for the research, Guy Perfect. I wasn’t talking console-specific, but that doesn’t matter.
So it seems somewhat historic for sei to just mean ‘set the appropriate bit in the status word’ instead of ‘set enable interrupt [bit]’ (which is how I read it up to now) and thus is opposite of what it means on many current platforms including AVRs…
Weird. Anyway, do you immediately jump after sei? If so, you might want to insert a nop and see if that helps. Some platforms will only disable interrupts after the next opcode.
cYa,
Tauwasser
Weird. Anyway, do you immediately jump after sei?
No, the code attempts to set up a call frame (below the frame set up to enter the jump_to_reset() C function) before the unconditional jump. This indicates to me that the absolute address called is SUPPOSED to end with a “jal” instruction.
Now that I think about it, the function I used to jump to the reset vector is probably not meant to be used with code that doesn’t expect to return.
And for once in my life, I didn’t mess up the SEI/CLI order :P.