Any help is greatly appreciated.
Thus posting this chit-chat as answer, because the comment section is annoying me by length limit plus formatting, and your "question" above makes this kind of "answer" sort of legit.
According to "Setting Interrupts" PDF document, on page 13, the 32 bit format of address is clear, in case of address 0x123456 the memory should contain bytes 56 34 12 00
(not sure if the last 00
can be any junk, or must be zero, I guess for future-compatibility zero is better, although I guess eZ80F91 will use only 24 bits, ignoring the last 8).
So the definition in your original question is very likely wrong, the 00
should be after the 3 bytes, not ahead.
The three byte .dl
should be enough with additional extra junk byte by .db
too (to avoid manual label decomposing into bytes).
I'm only familiar with classic Z80, so I don't have exact idea what is wrong with your code, but general principles and things which are probably worth a check:
check if the device has truly eZ80F91 ("-like" ??? who else is manufacturing some clone of it? I guess it's either original from Zilog or not, no "-like" possible), because any other eZ80 variant has only 8 bit I
register, and needs different interrupt table setup and interrupt handling (maybe try ld hl,0x1234
ld i,hl
ld hl,0
ld hl,i
and check the value in hl
if it is back to 0x1234
).
check the binary produced or in the debugger while the code is initializing vector table, that the memory contains values as expected
check the device description (if available), which kind of interrupts are fired and when, why do you actually expect button "on" to fire an interrupt? (for example ZX Spectrum - the Z80 machine I'm familiar with - has no keyboard interrupt, the keyboard must be polled by code, the only interrupt was fired upon start of vertical retrace of display beam, i.e. at ~50Hz with PAL/SECAM ZX models, and about ~60Hz with US NTSC variants, causing the games to run a tad faster on US ZX clones ... IIRC the TI calculators have timer interrupt, like 100Hz one, but I never studied them in depth, so this may be completely wrong info)
make sure you are in "ADL" mode, not in "Z80" compatibility mode.
your "FillScreen" routine is trying to return, but it doesn't seem to have proper interrupt-like prologue/epilogue, so wherever it will return, it did damage the content of registers, and it does not return through reti
.
you also return from SetInterrupt
routine into something... what is being run meanwhile, while your interrupt is installed?
You can first try "empty" interrupt handler like
FillScreen:
ei ; not sure if there's implicit DI - if yes, EI needed
reti
to see if the code running in main thread is ok (and your interrupt handler works). Mind you, if it's the ordinary calculator handler running, and it requires it's own interrupt handler for it's life, then just installing empty interrupt will hamper the functionality already ... maybe you shouldn't ever return from your code (the main thread, like where the setInterrupt is called) and do your own infinite main loop).
If you want to do more in your interrupt, you must preserve register values for the code in the main thread, for example if you know the main thread does NOT use alternate registers, then you can switch registers quickly by
interruptHandler:
di ; disable interrupts until done
; (especially if you know your interrupt may take longer to run)
; preserve current register values (by switching to alternate ones)
ex af,af
exx
; do your stuff here (destroying alternate register values)
; which is OK, if your interrupt handler is the only code using them
...
; restore the register values back (by switching to original ones)
exx
ex af,af
; return from interrupt
ei
reti
Or if you know there's always enough space on the stack, you use push/pop
to preserve original register values.
Or if the stack space may be too tense, but you have separate memory block which can be used as interrupt handler stack, you can switch to that first:
interruptHandler:
di
; preserve current stack pointer (self-modify code)
ld (interruptHandler_SP+1),sp
ld sp,top_of_interrupt_stack
; preserve registers as needed (AF with flags being a MUST)
push ...
; do your stuff here
...
; restore registers as needed
pop ...
; restore stack pointer
interruptHandler_SP:
ld sp,0x123456 ; this will be overwritten at start of handler
; return from interrupt
ei
reti
Thinking about it, your interrupt handler is obviously not interrupt handler, so even if it would run once correctly, that would be the last correct thing happening in your device.
Also filling the screen is a bit unfortunate choice (as it will take loooong time to finish and it's difficult to see it twice).
Maybe as a quick test do something like:
testInterrupt:
di
push af
; increment first byte of video ram to make some visible "noise"
ld a,(vRam)
inc a
ld (vRam),a
; restore flags, enable interrupts, return back to main code
pop af
ei
reti
And generally the interrupt handlers should be very fast and tiny, doing task like clearing vram should be left to main code, the interrupt should probably just set some global flag, that vram clearing is required (to finish within few T cycles), and then the main code can in loop test for various event flags, and react to the "clear vram" flag by clearing the vram. There shouldn't be any serious "biz" logic in the handlers, only collecting the state/data which is imminent (like data on serial bus I/O) into some flags/buffers, and letting the main code outside of interrupt to process such flags/buffers with the full logic.
Maybe even consider trying first some classic Z80, unless you really want eZ80F91 badly. The classic Z80 has ton of materials available, with different machines and emulators as it was very popular CPU (for example I'm sort of Z80 "expert" thanks to the ZX Spectrum computer, I did code several demos and games for it back around 1991-1996 years). So it will be easier to help with that (seems this question was unanswered long enough to guess there's not that many people coding for eZ80F91).