汇编 68k - 有效地从地址开始清除

assembly 68k - clear starting from address efficently

这是我将数据从 SCREEN 地址清除到 600 字节的代码段。

    lea SCREEN,a3
    move.w  #(600/4)-1,d0   ; bytes / 4 bytes (long)
clear_data:
   clr.l    (a3)+
   dbra d0,clear_data

这可行,但我想知道如何在不循环 600/4 次的情况下获得相同的结果。 基本上我想直接指向 SCREEN 并做类似

的事情
; point PC to SCREEN ?
dcb.b 600,0

有可能吗?

编辑POST回答

仍然使用软件代码,这个周期快了大约 2 倍(从 RamJam 课程中偷来的):

    lea SCREEN,a3
    move.w  #(600/32)-1,d0   ; bytes / 32 bytes (long*8)
clear_data:
    clr.l    (a3)+
    clr.l    (a3)+
    clr.l    (a3)+
    clr.l    (a3)+
    clr.l    (a3)+
    clr.l    (a3)+
    clr.l    (a3)+
    clr.l    (a3)+
    dbra d0,clear_data

然而,正如 Peter 在回答中提到的那样,使用 blitter(如果硬件提供)可以极大地优化性能。

不,在一个循环中一次存储 4 个字节可能是您所能得到的最好的了。也许展开一点以减少循环开销,如果那个紧密的循环不会在您关心的任何 m68k 硬件上最大化内存带宽。或者可能不是:@jasonharper 评论说后来的 m68k 芯片对 2 指令循环有特殊支持。


dcb.b 600,0 是一个 assemble 时间的东西,它 assembles 字节到你的输出文件中。

你不能 "run" 它在 运行 时间。请记住,asm 源代码不直接 运行;这是一种创建包含 m68k 机器代码 and/or 数据的二进制文件的方法。

您可以使用混合指令的数据指令来 "manually" 通过指定您想要的机器代码字节来编码指令,但是 600 个字节的零只会解码为一些 m68k 指令。 (我没有检查 00 00 如何在 m68k 上解码。)


一些基于 m68k 的计算机具有用于处理大内存块的硬件芯片。这通常称为 blitter chip (Wikipedia)。例如一些 Atari m68k 台式机,如 Mega STe,有一个 BLiTTER 芯片。

您可以 运行 在 CPU 上编写一些指令来编程 blitter 以在 CPU 继续 运行 时清除或复制一大块内存其他说明。这基本上是一个 DMA 复制引擎。

用MOVEM真正烧穿设置内存!

我建议你不要使用CLR.L;如果您查看时钟计时,您会发现它似乎效率很低。最好将要设置内存的值加载到寄存器中,然后 MOVE.L ,(A0)+

但是,为了极速,请使用MOVEM.L到set/clear大面积内存。它比使用 CLR 或标准 MOVE.L

快 2 到 3 倍

这是一个设置 64 字节块的子例程示例,然后设置任何剩余的长字,但可以自定义。

         ORG     00
         MOVE.L  #MEMSTART,A0        ; memory to clear
         MOVE.L  #ZEROS,A1           ; value to set memory to e.g. 0
         MOVE.L  #600,D7             ; number of bytes
         BSR     SETBLOCK
         STOP    #2700

SETBLOCK
         ; MOVEM doesn't support destination = (Ax)+, 
         ; does support destination = -(Ax)
         ADD.L   D7,A0               ; so start at end

         LSR.L   #2,D7               ; divide by 4 for Long words.
         MOVE.L  D7,D6
         LSR.L   #4,D6               ; # of 16 longword blocks 
         BEQ.S   NOBLOCK             ; branch if no none
         SUBQ.L  #1,D6               ; one less so DBRA works
         MOVEM.L (A1),D0-D4/A2-A4    ; 8 registers = 32 bytes 

ZAPBLOCK MOVEM.L D0-D4/A2-A4,-(A0)   ; 8 x 4 = 32 bytes
         MOVEM.L D0-D4/A2-A4,-(A0)   ; 8 x 4 again for 64 bytes
         DBRA    D6,ZAPBLOCK         ; loop ends when D7=-1
NOBLOCK  AND.W   #[=10=]F,D7             ; how many long words left
         BEQ.S   NONE
         ; do any remainder
         SUBQ.W  #1,D7               ; 1 less so DBRA works
         MOVE.L  (A1),D0             ; pattern in D0 if not there b4
ZAP      MOVE.L  D0,-(A0)            ; set memory long word at a time
         DBRA    D7,ZAP
NONE
         RTS

ZEROS    DC.L    0,0,0,0,0,0,0,0      ; 8x4 = 32
         ORG     00
MEMSTART DS.B    600

本例使用D0-D4和A2-A4分别得到8个寄存器一次设置32字节,重复两次为64字节。没有理由不能向 ZAPBLOCK 循环添加更多 MOVEM 指令,以便为每个循环迭代写入 128、256 或更多字节,相应地更改 LSR/ AND 指令。

请注意,DBRA 仅对字进行操作,因此这只会设置 65k x 块大小。这可以解决,例如使用 SUBQ 和 BGT,而不是 DBRA。

出于某种原因,我记得 CLR 指令在某些 68k 上进行了读取和写入

时机

比较 3 个备选方案,假设标准 68000 具有 16 位数据总线...

  1. 使用 CLR

    LOOP:  
           CLR (A0)+      12+8
            DBRA D7,LOOP   10/14
    

每个长字 30 个周期,每个长字 20 个,多次清除。

  1. 使用MOVE.L

        MOVEQ #0,D0        ; 4
    LOOP:
        MOVE.L D0,(A0)+    ; 12
        DBRA   D7,LOOP     ; 10/14
    

每个长字 22 个周期,每个长字 12 个周期,具有多个 MOVE.L 操作。

  1. 使用MOVEM.L

    LOOP:
        MOVEM.L  D0-D4/A2-A4,-(A0)    ;  8+8*8 = 72
        MOVEM.L  D0-D4/A2-A4,-(A0)    ;  8+8*8 = 72
        DBRA     D6,LOOP              ;  10/14
    

154 cycles/iteration 但只有 每个长字约 9.5 个周期 。这可能与硬件 blitter 的性能具有竞争力。

Vogomatix 方法 3 实际上很快,但没有声称的那么快。出于某种原因,他在此示例中省略了初始寄存器加载设置时间,这非常重要。

您必须添加 'Moveq #0,d0-4(16 个周期),然后是 4 个地址寄存器:12 个周期,每个 reg x 4 是 48,因此设置需要 64 个周期。

在 218 cyc/iteration 那就是每个长字约 13.6 个周期;不如解决方案 2 好。

我发现最快的方法是尽可能多地使用寄存器(通常使用 13 个),然后:

movem.l (An),a0-4/d0-7; 13 Longs    12+8n    = 116 cycles
movem.l a0-4/d0-7,(An);              8+8n   = 112 cycles

上面的 mem move (112 cycles) 行可以重复,但是需要多次写入。 cyc/long 速度并没有赶上前面示例的速度,直到在一个迭代循环中写入至少 48 个或更多,到那时我们刚好低于 12 cyc/long。所以例如128 个多头,我们得到 1300 个周期或大约 10.2 cyc/long。 写入越多,循环设置+执行滚动平均值就越低,直到它慢慢接近理论的 8+8n 循环/长限制。

您还可以通过使用所有寄存器 (16) 来进一步最大化迭代效率,但是通过使用 15 个寄存器和用于 SP 的寄存器 A7 的棘手解决方法,您会遇到更难的数据流控制的复杂性,如果使用所有 16.