13

不是作业问题,而是针对我正在开发的游戏。

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

高级概述:

  • hl指向四个颜色字节。[hl] = %gggrrrrr, [hl+1] = %0bbbbbgg,[hl+2] = %GGGRRRRR[hl+3] = %0BBBBBGG. (这是两种颜色,rgbRGB。)
  • bc指向三个增量字节。[bc] = %hhhhaaaa, [bc+1] = %ddddssss, 和[bc+2] = %ppppqqqq. (这是六个 delta 值,hadsp、 和q。)
  • 所以有六个 5 位颜色通道值和六个 4 位增量值。我想将每个颜色通道C与增量值D配对,并像这样改变C : C' = C + ( D & %11) - (( D & %1100) >> 2),但将C保持在其 5 内位界限 [0, 31]。我实际上并不关心它们是如何配对的:任何方便的一对一配对都可以。如果C + (( D & %1100) >> 2) - ( D & %11) 以某种方式允许更优雅的算法,我会同意的。

如果我在 register 中隔离一个颜色通道C和在register 中d的一个增量值De,那么这个例程将为该对执行变化:

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

到目前为止,我有一个通用例程,可以将任何 DV 应用于任何颜色通道;然后是三个隔离红色、绿色或蓝色通道并将给定 DV 应用于它们的例程;最后是一个主程序,它挑选出六个 DV 并与它们一起调用适当的通道修改程序。这“足够好”,但我敢肯定还有改进的余地。执行速度似乎不是问题,但我想减少代码大小(当然删除冗余指令也会提高一点速度)。是否有任何帮助的 asm 位操作技巧?

这是完整的代码:

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

VaryRedByDV:
;;; e = DV
;;; [hl+0] = gggr:rrrr
;;; [hl+1] = 0bbb:bbgg
; store red in d
    ld a, [hl]
    and %00011111
    ld d, a
; vary d according to e
    call GetColorChannelVariedByDV
; store a back in red
    ld d, a
    ld a, [hl]
    and %11100000
    or d
    ld [hl], a
    ret

VaryGreenByDV:
;;; e = DV
;;; [hl+0] = gggr:rrrr
;;; [hl+1] = 0bbb:bbgg
; store green in d
    ld a, [hli]
    and %11100000
    srl a
    swap a
    ld d, a ; d = 00000ggg
    ld a, [hld]
    and %00000011
    swap a
    srl a
    or d
    ld d, a
; vary d according to e
    call GetColorChannelVariedByDV
; store a back in green
    sla a
    swap a
    ld d, a
    and %11100000
    ld e, a
    ld a, d
    and %00000011
    ld d, a
    ld a, [hl]
    and %00011111
    or e
    ld [hli], a
    ld a, [hl]
    and %11111100
    or d
    ld [hld], a
    ret

VaryBlueByDV:
;;; e = DV
;;; [hl+0] = gggr:rrrr
;;; [hl+1] = 0bbb:bbgg
; store blue in d
    inc hl
    ld a, [hl]
    and %01111100
    srl a
    srl a
    ld d, a
; vary d according to e
    call GetColorChannelVariedByDV
; store a back in blue
    ld d, a
    sla d
    sla d
    ld a, [hl]
    and %10000011
    or d
    ld [hl], a
    dec hl
    ret

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

;;; LiteRed ~ hDV, aka, rrrrr ~ hhhh
; store hDV in e
    ld a, [bc]
    swap a
    and %1111
    ld e, a
; vary LiteRed by e
    call VaryRedByDV

;;; LiteGrn ~ aDV, aka, ggggg ~ aaaa
; store aDV in e
    ld a, [bc]
    and %1111
    ld e, a
; vary LiteGrn by e
    call VaryGreenByDV

;;; move from h/a DV to d/s DV
    inc bc

;;; LiteBlu ~ dDV, aka, bbbbb ~ dddd
; store dDV in e
    ld a, [bc]
    swap a
    and %1111
    ld e, a
; vary LiteBlu by e
    call VaryBlueByDV

;;; Move from Lite color to Dark color
    inc hl
    inc hl

;;; DarkRed ~ sDV, aka, RRRRR ~ ssss
; store sDV in e
    ld a, [bc]
    and %1111
    ld e, a
; vary DarkRed by e
    call VaryRedByDV

;;; move from d/s DV to p/q DV
    inc bc

;;; DarkGrn ~ pDV, aka, GGGGG ~ pppp
; store pDV in e
    ld a, [bc]
    swap a
    and %1111
    ld e, a
; vary DarkGrn by e
    call VaryGreenByDV

;;; DarkBlu ~ qDV, aka, BBBBB ~ qqqq
; store qDV in e
    ld a, [bc]
    and %1111
    ld e, a
; vary DarkBlu by e
    call VaryBlueByDV

    ret
4

2 回答 2

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
于 2017-05-16T11:36:15.110 回答
3

嗯...如果您可以进一步预处理它们,您应该向我们提供有关这些数据来自何处的更多信息,因为这+(d&3)-(d>>2)看起来很不幸,如果可能的话,我会尽量避免这种情况。实际上整个 5:5:5 RGB 的东西可能有点超过 Z80,但如果你知道它对你有用,那就继续吧(我说的是我的 ZX Spectrum 经验,其中 3.5MHz 还不够操作 1 位黑白像素)。

但是目前,您已经获得的内容可以通过删除两条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 中的结果加载到lorc和被夹住ld a,(hl/bc)。这是 4+7 t 而不是那些jr/cp/ret/.... 如果我没有计算错误(0 + 0 - 3 是最小值,31 + 3 - 0是最大的结果)。因此,您仍然可以将“页内”地址 35..252 处的字节用于其他数据或代码。

稍后我将尝试对其进行整体查看,以尽可能避免使用每个组件的通用内容,但恐怕更好的输入数据格式可能会给您带来更大的提升,或者了解您的总体目标以及所有约束(例如,如果 RGB 中的最高位始终为 0 且必须为 0,或者结果可以是随机的,并且输入为 0,等等......每个细节通常都会导致另一个被删除的指令,这通常是Z80 价值 4-11 吨)。

于 2017-05-15T19:50:08.217 回答