【问题标题】:Z80 Assembly (1MHz) CP/M: How to get correct physical input using buttonsZ80 Assembly (1MHz) CP/M:如何使用按钮获得正确的物理输入
【发布时间】:2018-11-03 21:02:50
【问题描述】:

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

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

我已经阅读了手册并且知道可以使用的一组说明(仅限基本必需品)。首先,我并不是要尽可能地获得最干净、最好看的代码。不过不用担心,我稍后会美化它,因为我喜欢干净高效的代码。

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

该练习具有以下规格:

  1. 起始地址 RAM:E000h
  2. 输入端口 1:03h
  3. 输出端口 1:05h
  4. 端口的 I/O 映射
  5. 电路自动打开 (1),因此 LED 处于低电平有效 (0)
  6. 输入 2、3、4 改变 LED 移动行为
  7. 输入 5,6 改变 LED 闪烁频率

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

我目前的问题是我不太确定如何正确输入物理输入 - IIRC,我需要通过使用 XOR 来指定一个位模式,所有内容都为 1 但所需的输入,这样我就可以使用我的程序中的信息。

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

因此,我想我会碰碰运气并向社区寻求帮助。

对于那些感兴趣的人,我会发布我的代码。正如我已经提到的,这是非常基本的,但我暂时只需要它来完成工作。由于我不喜欢大量格式笨拙的代码,因此我发布了文件here。该文件是通过 GoogleDrive 托管的 *.txt。

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

[EDIT] 根据用户 Ruud Helderman 的输入,在帖子中添加了特定代码

[EDIT] 更新了 *.txt 文件中的代码 - 现在更简单、更高效

[EDIT] 使用 HTML 格式来突出显示帖子中的指令

具体代码sn-p:

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 行为(在这种情况下:闪烁)和频率,总共八个开关(1 到 8,非激活状态 = 1;使用开关 2 到 6) .我知道获取输入应该是小菜一碟 - 它应该只是使用位模式为 0 和恰好一个 1 的 XOR。

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

我很清楚这可能是一个微不足道的问题,也许我只是陷入了我通过过度思考自己创造的心理循环中,但目前我不知道该怎么做(尽管我可以看到地平线上的城堡——感谢卡夫卡!)。

非常感谢任何帮助。

【问题讨论】:

  • 我可以理解您希望有人帮助您相处,但在 stackoverflow 中,一次问一个问题很重要。看来您主要关心的是按位测试;您可能想要编辑您的问题并专注于该问题。至于 if-then-else 结构,这对您来说应该不会太难,因为您之前已经使用过 JP 和 JPNZ。看看你的代码,我会说你到目前为止做得很好。只是不要在每个子程序开始时重置 SP;你正在扼杀你的退货地址。
  • 非常感谢您的意见。我将相应地编辑我的问题。关于 SP 不必要的额外初始化,我必须承认我不知道它是如何结束的。无论如何,感谢您的提醒;我的导师不会喜欢这样的!
  • 请注意,最好在问题中包含(相关部分)您的源代码,而不是链接到外部源。
  • 再次感谢您。我将相应地编辑我的帖子,并将在以后的帖子中直接包含特定的代码 sn-ps。我只需要了解所有不同的格式选项,因为我不希望我的代码看起来很笨重。好吧,我想我得在相应的部分多花点时间了!

标签: assembly z80 cpm


【解决方案1】:

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

实现 if/else 类型条件的常用方法是:

  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.

我不知道 TEST 在 8080 或 Z80 样式的语法中。

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

【讨论】:

  • 非常感谢您的回答!正如你所说,即使对我来说,语法似乎也有点“偏离”。网上提到了LD,但我们的手册使用MOV。不过,没有ANI,只有AND。关于TEST:这只是我绝望。但是有CP,所以我想我会找到一种方法来正确比较位模式并相应地设置标志。顺便说一句,这种“语法混合”的原因可能是我们使用了一个非常古老的电路板(“MC8”)来练习架构和理解组装的基础知识。但我离题了 - 再次感谢您,您的回答非常有帮助!
  • @OingoBoingo 据我所知,AND 是用于与寄存器或立即值进行与运算的 z80 语法,而 8080 语法区分 ANA(用于与另一个寄存器进行与运算)和ANI(和立即)。所以可能你的汇编程序会接受。 CP 进行减法而不是按位和,因此这取决于您是在寻找位模式还是幅度与另一个值。什么都行。
  • 为什么你需要一个转变来测试​​关于多个位的东西? ani 0f0h 如果所有 4 个高位都被清除,则设置 ZF,否则清除 ZF。或者你的意思是如果一个位被清除而另一个被设置?您应该能够 XOR 翻转一些位,然后 AND 创建一个全零或非全零条件。如果有超过 2 个分支目标(不仅仅是二元决策),那么是的,您可以在决策树中移动位以获取 switch
  • @PeterCordes 如果您有一个字节,其中每个位都是一个独立的输入,那么一系列移位和在进位清除时跳转允许您依次处理每个字节,而无需从任何地方重新加载字节。鉴于互联网所关注的资源,它很可能类似于操纵杆输入——测试位 0 以查看玩家是否向左推,然后测试位 1 以查看他们是否向上推,等等。
  • @Tommy:哦,对了,你说的是“按顺序”,所以是的,这与我在上一句中所说的相符,你不只是测试一个多位字段的值。
【解决方案2】:

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

如我所料,问题在于我陷入了一个误解,幸运的是,我的实验室合作伙伴纠正了这个错误。所以,毕竟,我们能够让我们的程序运行 - 最后一刻,而不是 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% 的开发时间 - 是的。教训学习:始终从顶部开始阅读并仔细阅读。)

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

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

原来如此!

再次感谢大家的帮助。

如果还有任何问题,请随时提问!

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 2015-12-06
    • 1970-01-01
    • 2014-04-12
    • 1970-01-01
    • 2022-09-27
    • 2010-10-09
    • 2022-12-04
    • 2016-10-15
    相关资源
    最近更新 更多