assembly bit-manipulation z80 gameboy

assembly - Optimización de un algoritmo de manipulación de bits en GameBoy Z80



bit-manipulation (2)

El más pequeño que se me ocurre en este momento es de 57 bytes:

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

La corrección del comentario de Ped7g solo cuesta 4 bytes para un total de 61 bytes:

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

Este no es un problema de tarea, es para un juego que estoy desarrollando.

Tengo dos colores RGB de 16 bits y me gustaría variar sus seis canales de acuerdo con otras seis cantidades de cuatro bits. El algoritmo es simple pero tedioso; Estoy buscando una manera de optimizarlo haciendo un trabajo más útil a la vez.

La descripción de alto nivel:

  • hl apunta a los cuatro bytes de color. [hl] = %gggrrrrr , [hl+1] = %0bbbbbgg , [hl+2] = %GGGRRRRR , y [hl+3] = %0BBBBBGG . (Son dos colores, rgb y RGB ).
  • bc apunta a los tres bytes delta. [bc] = %hhhhaaaa , [bc+1] = %ddddssss , y [bc+2] = %ppppqqqq . (Son seis valores delta, h , a , d , s , p y q )
  • Entonces, hay seis valores de canal de color de 5 bits y seis valores de delta de 4 bits. Quiero emparejar cada canal de color C con un valor delta D , y variar C como tal: C '' = C + ( D &% 11) - (( D &% 1100) >> 2), pero manteniendo C dentro de sus 5 -bit límites [0, 31]. En realidad, no me importa cómo están emparejados: cualquier combinación conveniente de uno a uno está bien. Y si C + (( D &% 1100) >> 2) - ( D &% 11) permite un algoritmo más elegante de alguna manera, estaría bien con eso.

Si aislo un canal de color C en el registro d un valor delta D en el registro e , entonces esta rutina hará la variación para ese par:

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

Hasta ahora tengo una rutina genérica que aplica cualquier DV a cualquier canal de color; luego tres rutinas que aíslan los canales rojo, verde o azul y les aplica un DV dado; y finalmente una rutina principal que selecciona los seis DV y llama a la rutina de modificación de canal apropiada con ellos. Esto es "suficientemente bueno", pero estoy seguro de que hay margen de mejora. La velocidad de ejecución no parece ser un problema, pero me gustaría reducir el tamaño del código (y, por supuesto, eliminar las instrucciones redundantes también mejorará un poco la velocidad). ¿Hay algún truco de manipulación de bits de asm que ayudaría?

Aquí está el código completo:

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


Hmm ... deberías darnos más información acerca de dónde provienen esos datos, si puedes preprocesarlos aún más, porque ese +(d&3)-(d>>2) parece desafortunado y trataría de evitar eso, si es posible . En realidad, todo el material RGB 5: 5: 5 es probablemente un poco exagerado, pero si sabes que funcionará para ti, adelante (estoy hablando de mi experiencia con ZX Spectrum, donde 3,5MHz fue apenas suficiente para manipular píxeles B & W de 1 bit).

Pero por el momento, lo que ya tienes puede simplificarse un poco al eliminar dos instrucciones:

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) ...

Y si no le falta memoria, puede crear una tabla de búsqueda 256B para fijar valores, por lo que, por ejemplo, debería mantener h o b el byte de dirección alta de la tabla, y el resultado en a se cargaría luego en l o c y sujetado por ld a,(hl/bc) . Que es 4 + 7 t en lugar de jr/cp/ret/... En realidad, solo necesitaría algunos valores de esos 256, de -3 a 34 (0..34 y 253..255) si no los calculé mal (0 + 0 - 3 es mínimo, y 31 + 3 - 0 es el resultado máximo). De modo que aún puede usar bytes en las direcciones "dentro de la página" 35..252 para otros datos o códigos.

Trataré de analizarlo en su totalidad más adelante, para evitar algunos de los elementos genéricos por componente si es posible, pero me temo que un mejor formato de datos de entrada probablemente te daría un impulso mayor, o para conocer tu objetivo general. y todas las restricciones (como si el bit superior en RGB es siempre 0 y debe ser 0, o puede ser aleatorio como resultado, y es 0 como entrada, etc ... cada detalle a menudo puede conducir a otra instrucción eliminada, que a menudo 4-11 t vale en Z80).