优化GameBoy Z80中的位操作算法

时间:2017-05-15 14:16:37

标签: assembly bit-manipulation z80 gameboy

这不是一个家庭作业问题,而是我正在开发的游戏。

我有两种16位RGB颜色,并希望根据其他六个四位数量改变它们的六个通道。算法简单但乏味;我正在通过一次做更多有用的工作来寻找优化它的方法。

高级概述:

  • #standardSQL SELECT (SELECT p.value FROM UNNEST(event_dim.params) p WHERE p.key = 'Purchased_item') AS Purchased_item, (SELECT p.value FROM UNNEST(event_dim.params) p WHERE p.key = 'Completed_levels') AS Completed_levels, ... 指向四个颜色字节。 hl[hl] = %gggrrrrr[hl+1] = %0bbbbbgg[hl+2] = %GGGRRRRR。 (这是两种颜色,[hl+3] = %0BBBBBGGrgb。)
  • RGB指向三个delta字节。 bc[bc] = %hhhhaaaa[bc+1] = %ddddssss。 (这是六个delta值,[bc+2] = %ppppqqqqhadsp。)
  • 因此有六个5位颜色通道值和六个4位增量值。我想将每个颜色通道 C 与增量值 D 配对,并改变 C ,如下所示: C' = C +( D &%11) - (( D &%1100)>> 2),但保持 C 在其5位边界[0,31]内。我实际上并不关心他们如何配对:任何方便的一对一配对都没问题。如果 C +(( D &%1100)>> 2) - ( D &%11)允许以某种方式提供更优雅的算法,我对此感到满意。

如果我在注册表q中隔离了 C 的颜色通道,并且delta值 D 在注册d中,此例程将执行该对的变体:

e

到目前为止,我有一个将任何DV应用于任何颜色通道的通用例程;然后是三个隔离红色,绿色或蓝色通道并将特定DV应用于它们的例程;最后是一个主程序,它选出六个DV并用它们调用适当的通道修改程序。这已经足够了#34;但我确信还有改进的余地。执行速度似乎不是问题,但我希望减少代码大小(当然,删除冗余指令也会提高速度)。是否有任何asm位操作技巧会有所帮助?

这里是完整的代码:

VaryColorChannelByDV:
; d = color, e = DV
; a <- d + (e & %11) - (e >> 2), clamped to [0, 31]
    ld a, e
    and %11   ; a <- (e & %11)
    add d   ; a <- d + (e & %11)
    srl e
    srl e   ; e <- e >> 2
    sub e   ; a <- d + (e & %11) - (e >> 2)
    jr c, .zero   ; a < 0, clamp to 0
    cp 32
    ret c   ; 0 <= a < 32
    ld a, 31   ; a >= 32, clamp to 31
    ret
.zero
    xor a
    ret

2 个答案:

答案 0 :(得分:9)

我现在能想出的最小值是57字节:

VaryColorsByDVs::
; hl = colors
; [hl+0] = gggr:rrrr
; [hl+1] = 0bbb:bbgg
; [hl+2] = GGGR:RRRR
; [hl+3] = 0BBB:BBGG
; bc = DVs
; [bc+0] = hhhh:aaaa
; [bc+1] = dddd:ssss
; [bc+2] = pppp:qqqq
    ld a, 2 ; -floor($100/3)*6 mod $100
.next:
    sla [hl]
    inc hl
    rl [hl]
.loop:
    push af
    rrca
    ld a, [bc]
    jr nc, .skip
    swap a
    inc bc
.skip:
    rlca
    ld d, a
    and %00011000
    ld e, a
    ld a, d
    rlca
    rlca
    and %00011000
    add a, [hl]
    jr nc, .noOverflow
    or %11111000
.noOverflow:
    sub e
    jr nc, .noUnderflow
    and %00000111
.noUnderflow:
    dec hl
    ld de, 5
.rotate:
    add a, a
    rl [hl]
    adc a, d
    dec e
    jr nz, .rotate
    inc hl
    ld [hl], a
    pop af
    add a, 85 ; floor($100/3)
    jr nc, .loop
    ret z
    inc hl
    jr .next

修复Ped7g的注释只需要4个字节,总共61个字节:

VaryColorsByDVs::
; hl = colors
; [hl+0] = gggr:rrrr
; [hl+1] = 0bbb:bbgg
; [hl+2] = GGGR:RRRR
; [hl+3] = 0BBB:BBGG
; bc = DVs
; [bc+0] = hhhh:aaaa
; [bc+1] = dddd:ssss
; [bc+2] = pppp:qqqq
    ld a, 2 ; -floor($100/3)*6 mod $100
.next:
    sla [hl]
    inc hl
    rl [hl]
.loop:
    push af
    rrca
    ld a, [bc]
    jr nc, .skip
    swap a
    inc bc
.skip:
    ld d, a
    and %00001100
    ld e, a
    ld a, d
    rlca
    rlca
    and %00001100
    sub e
    add a, a
    jr nc, .positive
.negative:
    add a, [hl]
    jr c, .continue
    and %00000111
    db $38 ; jr c,
.positive:
    add a, [hl]
    jr nc, .continue
    or %11111000
.continue:
    dec hl
    ld de, 5
.rotate:
    add a, a
    rl [hl]
    adc a, d
    dec e
    jr nz, .rotate
    inc hl
    ld [hl], a
    pop af
    add a, 85 ; floor($100/3)
    jr nc, .loop
    ret z
    inc hl
    jr .next

答案 1 :(得分:3)

嗯...你应该给我们更多关于这些数据来自哪里的信息,如果你可以进一步预处理它们,因为+(d&3)-(d>>2)看起来很不幸,如果可能的话我会尽量避免这种情况。实际上整个5:5:5 RGB的东西可能有点超过Z80的头部,但是如果你知道它对你有用,请继续(我在谈论我的ZX Spectrum经验,其中3.5MHz几乎没有足以操纵1位B&amp; W像素)。

但目前,您已经获得的内容可以通过删除两条ld指令立即进行简化:

VaryColorChannelByDV:
    ...
    add d
;    ld d, a   ; d <- d + (e & %11)
    srl e
    srl e
;    ld a, d   ;### A didn't change, still contains C + DV&3
    sub e   ; a <- d + (e & %11) - (e & %1100 >> 2)
    ...

如果您没有内存不足,可以创建256B查找表来钳制值,例如,您可以将hb保存在表的高地址字节中,然后将a中的结果加载到lc并由ld a,(hl/bc)加以限制。这是4 + 7 t而不是那些jr/cp/ret/...。你实际上只需要256个中的一些值,从-3到34(0..34和253..255),如果我没有计算错误(0 + 0 - 3是最小值,31 + 3) - 0是最大结果)。所以你仍然可以在地址&#34;页面内使用字节&#34;其他数据或代码为35..252。

我会尝试稍后整理一下,如果可能的话,尽量避免使用某些组件通用的东西,但我担心更好的输入数据格式可能会给你更大的提升,或者了解你的总体目标和所有约束(比如RGB中的顶部位总是0并且必须为0,或者可以是随机的结果,并且输入为0等等...每个细节通常会导致另一个被删除指令,在Z80上通常为4-11吨。