【问题标题】:Can I turn this into a loop through some 16-Bit Magic?我可以通过一些 16-Bit Magic 把它变成一个循环吗?
【发布时间】:2014-03-01 08:53:43
【问题描述】:

我现在开始使用 6502 程序集,但在处理需要处理大于 8 位数字的循环时遇到了问题。

具体来说,我想遍历一些内存位置。在伪 c 代码中,我想这样做:

    // 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 dec,这比我在 8 位 X 寄存器中实际存储的要多。

有没有优雅的方法来做到这一点?

【问题讨论】:

    标签: assembly 6502 c64


    【解决方案1】:

    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
    

    【讨论】:

    • 非常感谢!阅读间接模式,这让我望而却步。自我修改的代码也很有趣,我需要围绕旧机器的整个“代码=内存”的想法。
    • STC?? SEC,当然。顺便说一句,作为自我修改代码的可读性辅助工具,我使用 $C0DE 而不是 $FFFF 并让我的语法高亮显示它为黄色 - 可以很容易地发现你正在做某事的地方。
    • @EightBitGuru 抱歉让我的指令集混淆了。
    • 吹毛求疵:第一个代码sn-p中的第二个STA应该是$01。
    • 由于这个问题被标记为c64,因此您要避免使用零页地址$00$01,因为它们用于I/O 端口和6510 CPU 的ROM/RAM 配置.如果您希望通过 SYS 命令从 BASIC 安全地运行您的代码,最好的办法是使用 $FB$FE 范围。
    【解决方案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 
    

    几点说明:

    • 更快,因为减少了循环开销。由于更多命令而占用更多空间。

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

    可能与此不是 100% 相关,但这是另一种超过 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 寄存器而不是内存位置。

    【讨论】:

      猜你喜欢
      • 2015-04-24
      • 2018-09-17
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2017-03-03
      • 1970-01-01
      • 2020-04-24
      • 1970-01-01
      相关资源
      最近更新 更多