如何在MOS 6502汇编中创建延迟

3

我是ASM的新手,尝试理解以下代码如何创建延迟:

org $1000

loop: inc $d021
    jmp loop

是的,它只是一个改变前台程序。 - icodenosleep
你是指背景吗? - Michael
1
如果你所说的“delay”是指等待下一条扫描线,那么请添加一个内部循环来等待$d012的变化。 - Michael
不,我是指等待一秒钟,然后改变颜色(抱歉让你感到困惑)。 - icodenosleep
1
然后等待$d012改变320*50次,或者使用光栅中断来计算帧数。 - Michael
注意:某些编译器在标签使用冒号时会抛出错误。如果您收到“无效的操作数、标签或变量...”的错误,请尝试删除标签后面的冒号。例如,loop: 将更改为 loop。CBM Studio 编译器将给出此错误提示。 - Gavin Williams
4个回答

7

评论还算清晰明了。

每帧更改颜色的代码示例(每秒50次)

        sei       ; enable interrupts

loop1:  lda #$fb  ; wait for vertical retrace
loop2:  cmp $d012 ; until it reaches 251th raster line ($fb)
        bne loop2 ; which is out of the inner screen area

        inc $d021 ; increase background color

        lda $d012 ; make sure we reached
loop3:  cmp $d012 ; the next raster line so next time we
        beq loop3 ; should catch the same line next frame

        jmp loop1 ; jump to main loop

每秒改变颜色的代码示例

counter = $fa ; a zeropage address to be used as a counter

        lda #$00    ; reset
        sta counter ; counter

        sei       ; enable interrupts

loop1:  lda #$fb  ; wait for vertical retrace
loop2:  cmp $d012 ; until it reaches 251th raster line ($fb)
        bne loop2 ; which is out of the inner screen area

        inc counter ; increase frame counter
        lda counter ; check if counter
        cmp #$32    ; reached 50
        bne out     ; if not, pass the color changing routine

        lda #$00    ; reset
        sta counter ; counter

        inc $d021 ; increase background color
out:
        lda $d012 ; make sure we reached
loop3:  cmp $d012 ; the next raster line so next time we
        beq loop3 ; should catch the same line next frame

        jmp loop1 ; jump to main loop

你可以使用Y来替代零页和A。并且,将计数器减1而不是增加1,这样你就可以在Y上隐式BNE。更短、更快、更符合习惯用法。请查看我的编辑请求。 - youri
由于我喜欢编写256字节的介绍性代码,因此我使用了许多技巧,如自修改代码、非法操作码、共享寄存器值等。但是,这个代码示例是专门为一个说“我是汇编新手”的人编写的。因此,这不是大小或周期优化的源代码。这是一个面向初学者的代码示例,我相信反向循环可能会让他们感到困惑。 - Emir Akaydın
我认为使用零页使得编程比使用Y寄存器更加复杂,无论你如何循环(正向或反向),尤其是如果A用于两个目的。我的建议不是为了减少代码大小或周期数(这是一种副作用),而是为了增加可读性。也许使用前向Y寄存器,带有明确的CPY指令,会使初学者的解决方案更好。 - youri

0

这样怎么样?这应该会改变背景,等待4秒钟,然后再次更改。无限循环。

请注意,您可以将秒数更改为从0到255的任何值。

这是针对NTSC机器的,但您可以将60更改为PAL50

main:
    inc $D021

    ldx #4          //  Wait 4 seconds
loop1:
    ldy #60
loop2:

waitvb:
    bit $D011
    bpl waitvb
waitvb2:
    bit $D011
    bmi waitvb2

    dey
    bne loop2
    dex
    bne loop1

    jmp main

0

例如:

loop: ldx $d021
      inx
      stx $d021
      cpx #100
      bne loop

这并不是每秒更改 $d021,而是每15个周期更改一次,直到它的值为#100(这完全是可变的,因为我们不知道 $d021 的初始值)。 - Eight-Bit Guru
在最开始,user3049038只写了关于循环的事情,所以我为他写了一个循环。关于$d021是硬件寄存器的信息并没有更多的说明 - 所以我的回答是正确的。 - user2699113
$D020/01 对我们这些古老的恐龙来说是非常著名的地址,尤其与6502/10结合使用。 ;) - Devolus
也许是针对 C64 古董机的。我有 Atari 800xl,所以不了解这个寄存器 :) - user2699113

0
如果您可以确保代码不会跨越页面边界,一个有用的方法是在RAM中有一对字节来保存计算出的跳转地址,并使用间接跳转到以下内容:
TableStart:
    cmp #$C9
    cmp #$C9
    cmp #$C9
    cmp #$C9
    cmp #$C9
    ...
TableEnd:
    nop

如果跳转向量指向tableEnd,代码将在NOP后的七个周期后到达指令。如果它指向一个字节之前,则需要八个周期。两个字节之前,需要九个周期,以此类推。设置跳转向量可能需要一些时间,但延迟本身将可以平稳地从七个周期调整到任何更高的值,每个周期增加一个。标志位将被清除,但不会影响任何寄存器。


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