我能通过一些16位的魔法将这个转化为一个循环吗?

9

我现在开始学习6502汇编语言,但是在处理需要处理超过8位数字的循环时遇到了问题。

具体来说,我想要循环一些内存位置。用伪代码表示,我的目标是这样的:

    // Address is a pointer to memory
    int* address = 0x44AD;
    for(x = 0; x < 21; x++){
        // Move pointer forward 40 bytes
        address += 0x28;
        // Set memory location to 0x01
        &address = 0x01;
    }

从地址$44AD开始,我想要将$01写入RAM,然后跳转到前面$28,再将$01写入那里,然后再向前跳转$28,直到完成20次为止(最后要写的地址是$47A5)。

我的当前方法是循环展开,这很繁琐(即使我猜测汇编语言可以使它更简单):

ldy #$01
// Start from $44AD for the first row, 
    // then increase by $28 (40 dec) for the next 20
sty $44AD
sty $44D5
sty $44FD
    [...snipped..]
sty $477D
sty $47A5

我知道绝对寻址(使用累加器而不是Y寄存器-sta $44AD, x),但这只会给我一个0到255之间的数字。我真正需要的是像这样的东西:

       lda #$01
       ldx #$14 // 20 Dec
loop:  sta $44AD, x * $28
       dex
       bne loop

基本上,从最高地址开始,然后向下循环。问题是 $14 * $28 = $320 或 800 十进制,这比我实际可以存储在8位X寄存器中的更多。

有没有一种优雅的方法来解决这个问题?

2个回答

10

6502是一种8位处理器,所以你不能完全使用寄存器来计算16位地址。你需要通过页面零进行间接寻址。

      // set $00,$01 to $44AD + 20 * $28 = $47CD
      LDA #$CD
      STA $00
      LDA #$47
      STA $01

      LDX #20  // Loop 20 times
      LDY #0
loop: LDA #$01 // the value to store
      STA ($00),Y // store A to the address held in $00,$01
      // subtract $28 from $00,$01 (16-bit subtraction)
      SEC
      LDA $00
      SBC #$28
      STA $00
      LDA $01
      SBC #0
      STA $01
      // do it 19 more times
      DEX
      BNE loop

或者,您可以使用自修改代码。这通常是一种可疑的技术,但在像6502这样极为有限的嵌入式处理器上很常见。

      // set the instruction at "patch" to "STA $47CD"
      LDA #$CD
      STA patch+1
      LDA #$47
      STA patch+2

      LDX #20  // Loop 20 times
loop: LDA #$01 // the value to store
patch:STA $FFFF
      // subtract $28 from the address in "patch"
      SEC
      LDA patch+1
      SBC #$28
      STA patch+1
      LDA patch+2
      SBC #0
      STA patch+2
      // do it 19 more times
      DEX
      BNE loop

1
非常感谢!阅读间接模式的相关内容对我很有帮助。自修改代码也很有趣,我需要理解整个“代码=内存”概念,这是旧机器所具有的特点。 - Michael Stum
2
STC??SEC,当然。顺便说一下,在自修改代码中为了提高可读性,我使用$C0DE代替$FFFF,并让我的语法高亮显示器在黄色荧光下闪烁 - 这使得很容易发现你正在做一些棘手的事情的地方。 - Eight-Bit Guru
@EightBitGuru 抱歉,我把我的指令集搞混了。 - Raymond Chen
挑毛病:第一段代码片段中的第二个STA应该是$01。 - Anonym Mus
3
由于此问题被标记为 c64,因此您应避免使用零页地址 $00$01,因为它们由 6510 CPU 用于 I/O 端口和 ROM/RAM 配置。如果您希望通过 SYS 命令从 BASIC 安全地运行代码,则最好使用 $FB$FE 范围。 - Lars Haugseth
我曾经在C64上使用过自修改代码。当你需要它仅在一个计算机上运行时,虽然不优雅但却非常实用。 - David Crowell

2

复制1k数据的更高效方法:

    ldy #0
nextvalue:
    lda address, y
    sta address, y

    lda address+$100, y
    sta address+$100, y

    lda address+$200, y
    sta address+$200, y

    lda address+$300, y
    sta address+$300, y
    iny
    bne nextvalue 

几点说明:

  • 通过降低循环开销来实现更快的运行速度。由于需要更多命令,因此需要更多的空间。

  • 如果您使用的汇编器支持宏,您可以轻松地使其可配置,以处理代码处理的块数。

这可能与这个不完全相关,但这里有另一种实现超过255次循环的方法:

nextblock:
    ldy #0
nextvalue:
    lda address, y
    iny
    bne nextvalue

;Insert code to be executed between each block here:

    dec numblocks
    bpl nextblock

numblocks:
    .byte 3

几点说明:

  • 目前,代码并没有什么实际意义,但是循环“numblocks”次数。请“添加你自己的代码” :-)(通常我会与一些自修改的代码一起使用,例如增加sta、y地址)

  • bpl可能很危险(如果你不知道它是如何工作的),但在这种情况下工作得足够好(但如果numblocks地址包含足够大的值,则不会起作用)

  • 如果需要再次执行相同的代码,则需要重新设置numblocks。

  • 将numblocks放到零页中可以使代码稍微快一点。

  • 如果不需要用于其他用途(像通常一样),可以使用X寄存器代替内存位置。


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