在TI-84 Plus CE上设置中断

时间:2018-08-27 03:34:10

标签: assembly interrupt interrupt-handling texas-instruments z80

我正试图设置一个中断来捕捉按下ON按钮的情况。

到目前为止,这是我的代码:

SetInterrupt:
 di
  ; copy the 4 bytes from InterruptVectorTable to cursorImage
  ; (I chose cursorImage because it's on a 512-byte boundary, 0E30800h)
 ld   hl, InterruptVectorTable
 ld   de, cursorImage
 ld   bc, 4
 ldir
  ; clone the same 4 bytes into the rest of the 256-byte interrupt vector table
 ld   hl, cursorImage
 ld   de, cursorImage + 4
 ld   bc, 252
 ldir
  ; load the address of the new interrupt vector in the i register
  ;  and set interrupt mode to 2
 ld   hl, cursorImage >> 8 & 0ffffh
 ld   i, hl
 im   2
 ei
 ret
FillScreen:
  ; fills the screen with black pixels
 ld a, 0
 ld hl, vRam
 ld bc, 320*240*2
 call _MemSet
 ret
InterruptVectorTable:
  ; try to call FillScreen whenever there's an interrupt
 .db 00, FillScreen & 0ffh, FillScreen >> 8 & 0ffh, FillScreen >> 16 & 0ffh

但是,这只会冻结计算器(因为我无法使用任何键来停止程序)。

我相信问题出在InterruptVectorTable中。我真的不明白应该如何格式化表格。我在下面链接到的ez80应用说明说:“每个向量都是指向__vectptr段的4字节地址,”但是ez80使用24位地址,因此我不确定如何构造每个向量。

非常感谢您的帮助。


我已阅读/尝试阅读的参考文献:

1 个答案:

答案 0 :(得分:1)

  

非常感谢您的帮助。

因此发布此聊天作为答案,因为注释部分的长度限制和格式使我烦恼,并且您上面的“问题”使这种“答案”成为合法。

根据第13页的“设置中断” PDF文档,地址的32位格式是清除的,如果地址为0x123456,则内存应包含字节56 34 12 00(不确定最后一个{{1} }可以是任何垃圾,也可以是零,我想对于将来的兼容性,零更好些,尽管我猜eZ80F91将只使用24位,而忽略最后8位。

因此,原始问题中的定义很可能是错误的,00应该在3个字节之后,而不是前面。

三个字节00也应该足够了,.dl还要加上额外的垃圾字节(以避免手动标签分解为字节)。

我只熟悉经典的Z80,所以我不知道您的代码到底出了什么问题,而是一般的原理和可能值得一看的东西:

  • 检查设备是否真正具有eZ80F91(“-like” ???谁还在制造该产品的克隆?我猜它是否是Zilog的原始产品,是否可能不是“ -like”的产品),因为其他eZ80变体只有8位.db寄存器,并且需要不同的中断表设置和中断处理(可以尝试I ld hl,0x1234 ld i,hl ld hl,0并检查值在ld hl,i中返回到hl)。

  • 在代码初始化向量表时检查生成的二进制文件或在调试器中的二进制文件,以确认内存中包含预期的值

  • 检查设备说明(如果有),触发哪种中断以及何时触发,为什么您实际上希望按钮“ on”触发中断? (例如ZX Spectrum-我熟悉的Z80机器-没有键盘中断,必须通过代码轮询键盘,唯一的中断是在显示光束的垂直回扫开始时触发的,即使用PAL / SECAM时为〜50Hz ZX模型以及美国NTSC变体的大约60Hz,导致游戏在美国ZX克隆上运行得更快。IIRC TI计算器具有计时器中断,例如100Hz,但是我从未深入研究它们,所以这可能是完全错误的信息)

  • 确保您处于“ ADL”模式,而不是“ Z80”兼容模式。

  • 您的“ FillScreen”例程正在尝试返回,但是它似乎没有像中断一样的适当序言/结尾,因此无论返回什么地方,它确实损坏了寄存器的内容,并且不会通过0x1234返回。

  • 您还从reti例程返回到某种东西……同时在安装中断时正在运行什么?

您可以先尝试像“空”中断处理程序

SetInterrupt

查看在主线程中运行的代码是否正常(并且您的中断处理程序正常工作)。请注意,如果它正在运行普通的计算器处理程序,并且一生需要它自己的中断处理程序,那么仅安装空中断将已经妨碍了功能……也许您永远都不应从代码中返回(主线程,就像调用setInterrupt的位置一样)并执行自己的无限主循环)。

如果要在中断中执行更多操作,则必须保留主线程中代码的寄存器值,例如,如果您知道主线程不使用备用寄存器,则可以通过以下方式快速切换寄存器:

FillScreen:
    ei        ; not sure if there's implicit DI - if yes, EI needed
    reti

或者,如果您知道堆栈上总是有足够的空间,则可以使用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 来保留原始寄存器值。

或者如果堆栈空间可能太紧,但是您有单独的内存块可以用作中断处理程序堆栈,则可以先切换到该内存块:

push/pop

考虑到这一点,您的中断处理程序显然不是中断处理程序,因此即使它能够正确运行一次,也将是设备中发生的最后一件正确的事情。

填充屏幕也是个不幸的选择(因为这需要很长时间才能完成,而且很难两次看到它)。

也许作为一项快速测试可以做以下事情:

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

通常,中断处理程序应该非常小巧,应该将清除vram的工作留给主代码,中断可能应该只设置一些全局标志,要求清除vram(在几个T周期内完成) ,然后主代码可以循环测试各种事件标志,并通过清除vram对“ clear vram”标志作出反应。处理程序中不应有任何严肃的“ biz”逻辑,只能将即将发生的状态/数据(例如串行总线I / O上的数据)收集到一些标志/缓冲区中,并让中断之外的主代码进行处理具有完整逻辑的此类标志/缓冲区。


甚至可能考虑先尝试一些经典的Z80,除非您真的非常想要eZ80F91。经典的Z80拥有大量可用的材料,可以使用不同的机器和仿真器,因为它是非常流行的CPU(例如,由于ZX Spectrum计算机,我是Z80的“专家”,我曾为它编写了一些演示和游戏1991-1996年)。这样一来,解决起来会更容易(似乎这个问题没有得到足够长的答案,以至于猜测没有多少人在为eZ80F91编码)。