在汇编中编写while循环

9
我正在尝试使用6502处理器的汇编语言写一个while循环,但我无法弄清楚如何编写十六进制代码。我看到有些示例使用缩写来标记循环开始和结束的位置,但我没有看到实际的十六进制代码。
两个有用的代码是:
1. 将内存中的一个字节与X寄存器进行比较(缩写:CPX,十六进制:EC)。如果相等,则将Z标志设置为零。 2. 如果Z标志= 0,则跳转X个字节(缩写:BNE,十六进制:D0)

5
为什么你想要使用十六进制代码而不是汇编器? - Igor Skochinsky
@jnhyf "十六进制代码"?你使用哪个IDE、汇编器或编译器?你已经有多少关于6502汇编的经验了? - bzlm
2
我希望你并没有告诉我们你正在编写一份6502程序而不使用汇编器。你用什么开发系统?是Apple ][吗?还是其他的? - Pete Wilson
1
尝试编写一个编译器,以生成十六进制代码,以便在模拟的6502微处理器上运行。 - jnhyf
1
他可能正在使用微处理器培训器(例如http://innovision-group.net/catalog/images/8085_Microprocessor_Trainer_Kit_Model.jpg)。这些是简单的计算机,您可以直接用机器码编程。您仍然可以使用汇编器生成机器码,但仍需要手动输入机器码到系统中。 - Ferruccio
是的,除了使用单独的机器外,它在网页中进行模拟。代码会打印出被复制到系统中的十六进制代码。 - jnhyf
4个回答

6

这里有一个适合你开始学习的地方。该页面提供了一个交叉汇编器,可以在您的 PC 上运行。这 可能 是您的良好开发平台。

在进行任何操作之前,您必须了解 6502 的运作原理。然后,您需要了解包括以下内容的软件开发过程:

-- 准备所谓的符号指令的“源文件”,即您称之为“速记”
-- 使用汇编器将该源文件翻译成 6502 可以理解的机器指令
-- 将翻译加载到 6502 中
-- 告诉 6502 执行已翻译的机器指令

您的示例程序尝试从 SRC 复制 LEN 内存字节到 DST

您的格式如下:

      LDX #0    ; Start with the first byte 
_LOOP LDA SRC,X ; load a byte from SRC into the A register 
      STA DST,X ; store that byte into DST
      INX       ; bump the index register to point to the next SRC and DST locations 
      CPX #LEN  ; have we moved LEN characters?  
      BNE _LOOP ; if not, go move the next one

当你添加更多语句行(例如像END)之后,并且定义了SRCDSTLEN,你需要将整个内容保存在一个文件中,比如叫做cploop.txt

然后告诉汇编程序进行翻译。汇编程序输出一个二进制的6502机器码文件,可以表示为你所说的十六进制字节。

你将这个机器码文件输入到模拟的6502中。然后以某种方式告诉6502执行机器码所包含的操作。


谢谢,这有所帮助,但我仍不清楚像网站上的这个例子那样的东西如何表示: LDX #0; 从第一个字节开始 _LOOP LDA SRC,X; 移动它 STA DST,X INX; 然后提高指数... CPX #LEN; ...直到达到极限 BNE _LOOP "可以用十六进制表示。_loop标签在这里是重要的,但我不知道如何在实际的机器代码中表示这样的东西。 - jnhyf
@jnhyf 你真的需要将你的问题分成两部分:“我如何在6502机器代码中编写这段代码”和“我如何将该代码转换为十六进制表示法”。目前来看,很难帮助你。 - bzlm
@jnhyf -- 有两个步骤:(1)用汇编语言编写程序,也就是您所说的“速记”(顺便说一下,我从未听过这个术语);(2)将汇编语言文件交给汇编器,然后它会输出您想要的十六进制代码。 - Pete Wilson
2
顺便提一下,递减循环更高效 - 它们省去了比较指令的需要,并且除了节省这两个字节外,每次迭代还可以快2-6个时钟周期(取决于比较和寻址模式): LDX #LEN ; 从最后一个字节开始_LOOP LDA SRC,X ; 从SRC中加载一个字节到A寄存器中 STA DST,X ; 将该字节存储到DST中 DEX ; 增加索引寄存器,指向下一个SRC和DST位置 BNE _LOOP ; 如果不为零,则继续移动下一个 - Eight-Bit Guru
2
@jnhyf - 你似乎卡在汇编代码中"_LOOP"标签的存在上了。这只是一个汇编器指令,表示指令开始位置的地址。它不会被组装成任何输出到机器代码中的东西。当它作为操作数的一部分(即汇编指令的参数)时,它将被替换为定义时所在指令的地址。在6502上,最低有效字节先写出 - 地址$0300在机器码中写出为“00 03”。 - Rob Heiser

3
这是一个示例,展示了汇编代码(也称为“速记”)和机器码之间的对应关系。首先,这里是算法的汇编代码,一些参数被抽象化了:
* = 4000          ; This is the address where our program will be stored

      LDX #len
loop  LDA src,X 
      STA dest,X 
      DEX       
      BNE loop

当然,你不能直接将其转换为机器码。你还需要填写lensrcdest的值:
src = $1234
dest = $5678
len = 10

关于loop名称需要理解的是,就像src被分配了值$1234一样,loop将被分配为其后面指令的地址。因此,在这种情况下,由于LDX#len占用2个字节(我很快会向您展示),loop设置为$4000 + 2 = $4002。这是由汇编器自动完成的,但当然您也可以在纸上完成所有这些工作。
那么上述汇编程序的6502机器码是什么?
A2 0A
BD 34 12
9D 78 56
CA
D0 F7

我怎么知道这个呢?嗯,我刚刚将上面的程序粘贴到了http://www.masswerk.at/6502/assembler.html在线6502汇编器中。它甚至向你展示了汇编和机器码之间的详细映射:
4000        LDX #LEN        A2 0A
4002 LOOP   LDA SRC,X       BD 34 12
4005        STA DEST,X      9D 78 56
4008        DEX             CA
4009        BNE LOOP        D0 F7
400B

注意实际值LOOP并未用于计算BNE LOOP的机器码,只用于与BNE指令本身的相对地址进行比较: F7是-9,而$400B$4002之间的差为-9!

因此,如果您手动执行此操作,只需将其他所有内容转换为机器码,然后当您到达跳转时,计算下一条指令的起始地址与跳转目标地址之间的差。它应该为负数表示向后跳转,为正数表示向前跳转。


2
分支指令采用单字节有符号相对地址操作数,该操作数加上下一条指令的地址即为分支目标地址。由于分支指令总是占用2个字节,因此目标地址为分支指令地址加上(符号扩展的)操作数再减去2。
例如:
$D0 $00:无操作,不考虑条件分支到下一条指令
$D0 $FE:分支指向自身,如果Z=0则创建一个无限循环。

点赞解码并回答措辞不当的问题。;-) - Nick Westgate

1

while语句的真正含义是:

  1. 测试条件
  2. 如果条件为假,则转到5
  3. 执行某些操作
  4. 返回1(简单的JMP或分支)
  5. 程序的其余部分

使用6502,除非您可以做出很多假设,否则这些都不会非常简单。如果您要测试的条件始终是一个寄存器,则比较指令(cmp、cpx、cpy)和分支指令显然是您需要的1。

如果它将成为存储在内存中的单个字节,则需要加载该字节,然后进行比较。

如果它是存储在两个字节中的16位值,则需要加载并测试每个字节的值。

处理浮点数?如果您编写了或可用于您的浮点包(例如Commodore 64 ROM BASIC浮点例程),则需要使用它们。

您可以看到为什么高级语言具有数据类型。

因此,实际上取决于您正在处理的数据类型,但是在6502中的任何while实现应基本遵循上述流程。

如果您知道要比较的数据始终在X中,并且目标始终与+127/-128字节相差(Bxx指令的范围限制),则您在问题中确定的特定情况是可行的。


网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接