Z80 Assembly (1MHz) CP/M:如何使用按钮获得正确的物理输入

Z80 Assembly (1MHz) CP/M: How to get correct physical input using buttons

我是一名学习计算机科学的大一学生。在计算机工程中,我们正在研究 Zilog Z80 8 位微处理器 (1MHz) 和一组需要使用面包板和电缆手动连接的组件。

连接部分并不让我担心,但我确实有关于我需要编写的汇编程序的问题,以使我的程序正常工作(LED 运行宁光,手动输入行为和频率).

我已经阅读了手册并知道可以使用的指令集(仅是基本必需品)。首先,我并不是要尽可能获得最干净、最好看的代码;不过不用担心,我稍后会对其进行美化,因为我喜欢简洁高效的代码。

目前,该程序似乎 运行 在模拟器中运行良好,因此语法似乎没问题。不过,我不确定如何解决某些逻辑问题。

练习有以下规格:

  1. RAM起始地址:E000h
  2. 输入端口1:03h
  3. 输出端口1:05h
  4. I/O-mapping 端口
  5. 电路自动打开 (1) 所以 LED LOW-active (0)
  6. 输入 2、3、4 改变 LED 移动行为
  7. 输入 5,6 改变 LED 闪烁频率

我已经使用 ORG E000h 设置了起始地址并使用 MOV SP,FFFFh 初始化了堆栈指针。对于输入(三种不同类型的blinking/running,以及两种不同的频率,总共等于五个按钮)我创建了不同的标签。

我现在的问题是我不太确定如何让我的物理输入正确 - IIRC,我需要通过使用 XOR 来指定一个位模式,除了所需的输入之外的所有内容都是 1,这样我可以在我的程序中使用这些信息。

但即使我确实知道它的工作方式(至少我认为我知道),我还是无法完全理解软件实现。另外,我的条件有问题:按下一个开关将闪烁频率更改为 1/4 Hz,而按下另一个将其更改为 4Hz。在 higher-level 语言中,我在这里只使用 IF/ELSE,但我不知道在这种情况下该怎么做 - 遗憾的是,该手册仅包含基本操作,所以我不知所措。

因此,我想试试运气,向社区寻求帮助。

如果有兴趣,我会 post 我的代码。正如我已经提到的,这是非常基本的,但我只需要它来暂时完成工作。因为我不喜欢大块笨拙的格式化代码,所以我 post 编辑了文件 here。该文件是通过 GoogleDrive 托管的 *.txt。

感谢您的宝贵时间,祝您愉快!

[编辑] 根据用户 Ruud Helderman

的输入在 post 中添加了特定代码

[编辑] 更新了 *.txt-file 中的代码 - 现在更简单、更高效

[EDIT] 使用 HTML-formatting 突出显示 post

中的指令

具体代码片段:

blink:       ;function: all LED blinking, activated via input[2]
MOV A,FFh
OUT 05h,A     ;all LED out
CALL pause1   ;frequency 1/4Hz, activated via input[5]
MOV A,00h
OUT 05h,A     ;all LED on
CALL pause1
JP blink      ;jump back to begin of function

以上函数改变了 LED 行为(在本例中:闪烁),还改变了输入开关板上不同特定物理开关的频率,总共有 8 个开关(1 到 8,未激活状态 = 1;开关 2 到 6 是用过的)。我知道获取输入应该是小菜一碟——它应该只是使用位模式为 0 和一个 1 的 XOR 的问题。

在尝试为我的问题寻找解决方案时,我在网上发现了不同的方法,例如使用 TEST 检查特定位置上的位。不过,我的指导手册没有提到任何这样的指令,作业本身也没有提到它。

我很清楚这可能是一个微不足道的问题,也许我只是陷入了我自己因过度思考而造成的心理循环,但目前我不知道如何到达我需要的地方成为(尽管我可以在 horizon 上看到城堡 - 谢谢卡夫卡!)。

非常感谢任何帮助。

首先要注意的是:如果您使用的是 MOV,那么您可能使用的是 8080 语法而不是 Z80 语法。由于历史法律原因,Z80 不仅扩展了 8080 的汇编语言,它还重命名了所有现有的助记符(例如,MOVLD)。如果您正在搜索 Z80 代码并找到您不认识的指令,那很可能是其中的一部分。

实现if/else-type条件的常用方法是:

  1. 以适当的方式执行任何设置状态标志的操作;和
  2. 使用条件跳转之一来跳过某些代码或不跳过,具体取决于状态标志。

在你的情况下,你想做某事或不做某事取决于是否设置了某个位,所以一种方法是 ANI (z80: AND)。它计算累加器和操作数的逻辑与,将其存储在累加器中,但除其他外,它还设置了零标志。因此,您可以使用 JNZ (/JP NZ) 和 JZ (/JP Z) 根据是否设置位来做某事或不做某事。例如

; upon entry, A has an unknown value, loaded from somewhere.

ANI 08h    ; Set a = a & 8; so either bit 2 was originally set and a now
           ; has the value 8, or bit 2 wasn't set and a now has the value 0.

           ; Also: the zero flag is now set if a is zero, reset otherwise.

           ; So you've loaded NOT (a.bit2) into the zero flag.

           ; You've also lost the rest of the accumulator, but such is life.
           ; Keep a copy somewhere, or grab it again via IN as required.

JZ bitnotset

; code here will be performed only if bit 2 was originally unset.

bitnotset:

; this code will happen regardless of whether bit 2 was set.

我不知道 8080 或 Z80 风格的语法中有 TEST

您可能还会看到一种破坏性较小且效率稍高的解决方案,通过将它们移入进位位来按顺序测试字节中的多个位。这是另一种选择,但不一定值得担心,除非你的课程笔记强烈暗示这是你应该关注的方向。

经过几天的思考和绞尽脑汁,在大家的帮助下,我终于找到了解决问题的办法。最后,我最担心的是我不知道如何正确检查输入。

如我所料,问题出在我的错误观念上,幸运的是我的实验室伙伴纠正了我的错误观念。所以,毕竟,我们能够让我们的程序运行 - 最后一分钟而不是 100%,但它运行并满足要求。

那么我的误解是什么?有趣的是,我知道我们必须去哪里,而且这个想法是正确的。问题是我跳过了逻辑运算的主要部分——我已经在脑海中计算过了,然后将实际工作的解决方案与一个过时的 AND 结合起来,这破坏了功能。

总而言之,XORAND 的正确组合如下:

programloop:
MOV A,40h       ;state of button 2, inverted (XOR FFh)
MOV B,A         ;save state to register B
IN A,03h        ;input at port-address 03h
AND B           ;find out if button is pressed
JPNZ blink      ;if yes, jump to blink

MOV A,20h       ;state of button 3, inverted (XOR FFh)
MOV B,A         ;save state to register B
IN A,03h        ;input at port-address 03h
AND B           ;find out if button is pressed
JPNZ goright    ;if yes, jump to goright

MOV A,10h       ;state of button 4, inverted (XOR FFh)
MOV B,A         ;save state to register B
IN A,03h        ;input at port-address 03h
AND B           ;find out if button is pressed
JPNZ goleft     ;if yes, jump to goleft
JP programloop  ;go back to beginning (input has to be checked constantly)

这对改变 LED 行为的三个按钮起到了作用。

关于频率,由于时间紧迫,我们不得不将复杂性降低到只有两个状态(我们误读了作业并错误地从奖励问题开始,这花费了我们大约 50% 的开发时间 - 是的,我们. 经验教训:始终 从顶部开始阅读并仔细阅读。)

但是由于频率的改变起作用了,所以结果是 OK。

MOV A,03h       ;state of button five being pressed (inverted)
MOV B,A         ;saved state into register B for later use
IN A,03h        ;physical input over button
AND B           ;find out if button is pressed
JPNZ freq025Hz  ;if yes, jump to freq025Hz
JPZ freq4Hz     ;if no, jump to freq4Hz

就是这样!

再次感谢大家的帮助。

如有任何问题,欢迎随时提问!