// ____________________________ // ██▀▀█▀▀██▀▀▀▀▀▀▀█▀▀█ │ ▄▄▄ ▄ ▄▄ // ██ ▀ █▄ ▀██▄ ▀ ▄█ ▄▀▀ █ │ ▀█▄ ██▀▄ ██▄▀ ▄ ██▀ ▄███ // █ █ █ ▀▀ ▄█ █ █ ▀▄█ █▄ │ ▄▄█▀ ██▀ ██ ██ ▀█▄ ▀█▄▄ // ▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀────────┘ ▀▀ // by Guillaume 'Aoineko' Blanchard under CC BY-SA license //───────────────────────────────────────────────────────────────────────────── // Sprite tool module //───────────────────────────────────────────────────────────────────────────── #include "msxgl.h" #include "sprite_fx.h" //============================================================================= // READ-ONLY DATA //============================================================================= #if (SPRITEFX_USE_CROP) // Mask for left croping const u8 g_SpriteMaskL[8] = { 0b01111111, 0b00111111, 0b00011111, 0b00001111, 0b00000111, 0b00000011, 0b00000001, 0b00000000, }; // Mask for right croping const u8 g_SpriteMaskR[8] = { 0b11111110, 0b11111100, 0b11111000, 0b11110000, 0b11100000, 0b11000000, 0b10000000, 0b00000000, }; #endif // (SPRITEFX_USE_CROP) //============================================================================= // FUNCTIONS //============================================================================= //----------------------------------------------------------------------------- // Cropping //----------------------------------------------------------------------------- #if (SPRITEFX_USE_CROP) #if (SPRITEFX_USE_8x8) //----------------------------------------------------------------------------- // Crop 8x8 sprite left side void SpriteFX_CropLeft8(const u8* src, u8* dest, u8 offset) __NAKED // Stack: 3 bytes { src; // HL dest; // DE offset; // SP[2] __asm ld iy, #2 // Get 'offset' from the stack add iy, sp ld c, 0(iy) // C = offset // u8 mask = g_SpriteMaskL[offset]; ld b, #0 ld iy, #_g_SpriteMaskL add iy, bc ld c, 0(iy) // C = g_SpriteMaskL[offset] // loop (i, 8) // *dest++ = *src++ & mask; ld b, #8 loopCropL8: ld a, (hl) inc hl and c ld (de), a inc de djnz loopCropL8 pop hl inc sp // Adjusting stack address jp (hl) // Return to caller __endasm; } #endif // (SPRITEFX_USE_8x8) #if (SPRITEFX_USE_16x16) //----------------------------------------------------------------------------- // Crop 16x16 sprite left side void SpriteFX_CropLeft16(const u8* src, u8* dest, u8 offset) __NAKED // Stack: 3 bytes { src; // HL dest; // DE offset; // SP[2] __asm ld iy, #2 // Get 'offset' from the stack add iy, sp ld a, 0(iy) // A = offset // if (offset < 8) { cp #8 jp nc, greaterCropL16 // u8 mask = g_SpriteMaskL[offset]; ld b, #0 ld c, a // C = offset ld iy, #_g_SpriteMaskL add iy, bc ld c, 0(iy) // C = g_SpriteMaskL[offset] // loop (i, 16) // *dest++ = *src++ & mask; ld b, #16 loopCropL16_1: ld a, (hl) inc hl and c ld (de), a inc de djnz loopCropL16_1 // loop (i, 16) // *dest++ = *src++; ld bc, #16 ldir jp endCropL16 // } else { greaterCropL16: // loop (i, 16) // { // *dest++ = 0; // src++; // } sub #8 ld c, a // C = offset - 8 {computed this while A value is available} xor a ld b, #16 loopCropL16_2: inc hl ld (de), a inc de djnz loopCropL16_2 // u8 mask = g_SpriteMaskL[offset - 8]; ld iy, #_g_SpriteMaskL add iy, bc // B = 0 {from djnz} | C = offset - 8 {computed previously} ld c, 0(iy) // C = g_SpriteMaskL[offset - 8] // loop (i, 16) // *dest++ = *src++ & mask; ld b, #16 loopCropL16_3: ld a, (hl) inc hl and c ld (de), a inc de djnz loopCropL16_3 // } endCropL16: pop hl inc sp // Adjusting stack address jp (hl) // Return to caller __endasm; } #endif // (SPRITEFX_USE_16x16) #if (SPRITEFX_USE_8x8) //----------------------------------------------------------------------------- // Crop 8x8 sprite right side void SpriteFX_CropRight8(const u8* src, u8* dest, u8 offset) __NAKED // Stack: 3 bytes { src; // HL dest; // DE offset; // SP[2] __asm ld iy, #2 // Get 'offset' from the stack add iy, sp ld c, 0(iy) // C = offset // u8 mask = g_SpriteMaskR[offset]; ld b, #0 ld iy, #_g_SpriteMaskR add iy, bc ld c, 0(iy) // C = g_SpriteMaskL[offset] // loop (i, 8) // *dest++ = *src++ & mask; ld b, #8 loopCropR8: ld a, (hl) inc hl and c ld (de), a inc de djnz loopCropR8 pop hl inc sp // Adjusting stack address jp (hl) // Return to caller __endasm; } #endif // (SPRITEFX_USE_8x8) #if (SPRITEFX_USE_16x16) //----------------------------------------------------------------------------- // Crop 16x16 sprite right side void SpriteFX_CropRight16(const u8* src, u8* dest, u8 offset) __NAKED // Stack: 3 bytes { src; // HL dest; // DE offset; // SP[2] __asm ld iy, #2 // Get 'offset' from the stack add iy, sp ld a, 0(iy) // A = offset // if (offset < 8) { cp #8 jp nc, greaterCropR16 // loop (i, 16) // *dest++ = *src++; ld bc, #16 ldir // u8 mask = g_SpriteMaskR[offset]; ld c, a // C = offset ld iy, #_g_SpriteMaskR add iy, bc // B = 0 {from ldir} | C = offset ld c, 0(iy) // C = g_SpriteMaskR[offset] // loop (i, 16) // *dest++ = *src++ & mask; ld b, #16 loopCropR16_1: ld a, (hl) inc hl and c ld (de), a inc de djnz loopCropR16_1 jp endCropR16 // } else { greaterCropR16: // u8 mask = g_SpriteMaskR[offset - 8]; sub #8 ld c, a // C = offset - 8 ld b, #0 ld iy, #_g_SpriteMaskR add iy, bc ld c, 0(iy) // C = g_SpriteMaskR[offset - 8] // loop (i, 16) // *dest++ = *src++ & mask; ld b, #16 loopCropR16_2: ld a, (hl) inc hl and c ld (de), a inc de djnz loopCropR16_2 // loop (i, 16) // *dest++ = 0; xor a ld b, #16 loopCropR16_3: ld (de), a inc de djnz loopCropR16_3 // } endCropR16: pop hl inc sp // Adjusting stack address jp (hl) // Return to caller __endasm; } #endif // (SPRITEFX_USE_8x8) #if (SPRITEFX_USE_8x8) //----------------------------------------------------------------------------- // Crop 8x8 sprite top border. void SpriteFX_CropTop8(const u8* src, u8* dest, u8 offset) __NAKED // Stack: 3 bytes { src; // HL dest; // DE offset; // SP[2] __asm // u8 n = ++offset; ld iy, #2 // Get 'offset' from the stack add iy, sp ld c, 0(iy) inc c // C = offset + 1 // loop (i, n) // { // *dest++ = 0; // src++; // } xor a ld b, c // B = offset + 1 loopCropT8: ld (de), a inc hl inc de djnz loopCropT8 // for (u8 i = n; i < 8; ++i) // *dest++ = *src++; ld a, c // A = C = offset + 1 neg add a, #8 // A = 8 - C = 7 - offset jp z, skipCropT8 ld c, a // B = 0 {from djnz} ldir skipCropT8: pop hl inc sp // Adjusting stack address jp (hl) // Return to caller __endasm; } #endif // (SPRITEFX_USE_8x8) #if (SPRITEFX_USE_16x16) //----------------------------------------------------------------------------- // Crop 16x16 sprite top border. void SpriteFX_CropTop16(const u8* src, u8* dest, u8 offset) __NAKED // Stack: 3 bytes // can be optimized? { src; // HL dest; // DE offset; // SP[2] __asm // u8 n = ++offset; ld iy, #2 // Get 'offset' from the stack add iy, sp ld a, 0(iy) inc a // A = offset + 1 ld______iyl_a // IYL = A {backup} // loop (i, n) // { // *dest++ = 0; // src++; // } ld b, a // B = offset + 1 xor a loopCropT16_1: inc hl ld (de), a inc de djnz loopCropT16_1 // for (u8 i = n; i < 16; ++i) // *dest++ = *src++; ld______a_iyl neg add a, #16 // A = 16 - IYL = 15 - offset jp z, skipCropT16_1 ld c, a // B = 0 {from djnz} ldir skipCropT16_1: // loop (i, n) // { // *dest++ = 0; // src += n; // } ld______a_iyl // A = offset + 1 ld b, a // B = offset + 1 xor a loopCropT16_2: inc hl ld (de), a inc de djnz loopCropT16_2 // for (u8 i = n; i < 16; ++i) // *dest++ = *src++; ld______a_iyl neg add a, #16 // A = 16 - IYL = 15 - offset jp z, skipCropT16_2 ld c, a // B = 0 {from djnz} ldir skipCropT16_2: pop hl inc sp // Adjusting stack address jp (hl) // Return to caller __endasm; } #endif // (SPRITEFX_USE_16x16) #if (SPRITEFX_USE_8x8) //----------------------------------------------------------------------------- // Crop 8x8 sprite bottom border. void SpriteFX_CropBottom8(const u8* src, u8* dest, u8 offset) __NAKED // Stack: 3 bytes { src; // HL dest; // DE offset; // SP[2] __asm // u8 n = 7 - offset; ld iy, #2 // Get 'offset' from the stack add iy, sp ld a, 0(iy) neg add #7 // A = 7 - offset jp z, skipCropB8 // loop (i, n) // *dest++ = *src++; ld c, a ld b, #0 ldir // for (u8 i = n; i < 8; ++i) // *dest++ = 0; neg skipCropB8: add a, #8 ld b, a // B = 8 - A = offset + 1 xor a loopCropB8: ld (de), a inc de djnz loopCropB8 pop hl inc sp // Adjusting stack address jp (hl) // Return to caller __endasm; } #endif // (SPRITEFX_USE_8x8) #if (SPRITEFX_USE_16x16) //----------------------------------------------------------------------------- // Crop 16x16 sprite bottom border. void SpriteFX_CropBottom16(const u8* src, u8* dest, u8 offset) __NAKED // Stack: 3 bytes { src; // HL dest; // DE offset; // SP[2] __asm // u8 n = 15 - offset; ld iy, #2 // Get 'offset' from the stack add iy, sp ld a, 0(iy) neg add #15 // A = 15 - offset ld______iyl_a // IYL = A (backup) jp z, skipCropB16_1 // loop (i, n) // *dest++ = *src++; ld c, a ld b, #0 ldir // for (u8 i = n; i < 16; ++i) // { // *dest++ = 0; // src++; // } neg skipCropB16_1: add a, #16 ld b, a // B = 16 - A = offset + 1 xor a loopCropB16_1: inc hl ld (de), a inc de djnz loopCropB16_1 // loop (i, n) // *dest++ = *src++; ld______a_iyl // Retore A or a jp z, skipCropB16_2 ld c, a // B = 0 {from djnz} ldir // for (u8 i = n; i < 16; ++i) // *dest++ = 0; neg skipCropB16_2: add a, #16 ld b, a // B = 16 - A = offset + 1 xor a loopCropB16_2: ld (de), a inc de djnz loopCropB16_2 pop hl inc sp // Adjusting stack address jp (hl) // Return to caller __endasm; } #endif // (SPRITEFX_USE_16x16) #endif // (SPRITEFX_USE_CROP) //----------------------------------------------------------------------------- // Flipping //----------------------------------------------------------------------------- #if (SPRITEFX_USE_FLIP) #if (SPRITEFX_USE_8x8) //----------------------------------------------------------------------------- // Vertical flip 8x8 sprite void SpriteFX_FlipVertical8(const u8* src, u8* dest) __NAKED __PRESERVES(iyl, iyh) { src; // HL dest; // DE __asm ld bc, #7 add hl, bc ld b, #8 flipLoopV: ld a, (hl) dec hl ld (de), a inc de djnz flipLoopV ret __endasm; } #endif #if (SPRITEFX_USE_16x16) //----------------------------------------------------------------------------- // Vertical flip 16x16 sprite void SpriteFX_FlipVertical16(const u8* src, u8* dest) __NAKED __PRESERVES(iyl, iyh) { src; // HL dest; // DE __asm ld bc, #15 add hl, bc ld b, #16 flipLoopV1: ld a, (hl) dec hl ld (de), a inc de djnz flipLoopV1 ld c, #32 // B is 0 because of djnz add hl, bc ld b, #16 flipLoopV2: ld a, (hl) dec hl ld (de), a inc de djnz flipLoopV2 ret __endasm; } #endif #if (SPRITEFX_USE_8x8) //----------------------------------------------------------------------------- // Horizontally flip 8x8 sprite void SpriteFX_FlipHorizontal8(const u8* src, u8* dest) __NAKED __PRESERVES(iyl, iyh) { src; // HL dest; // DE __asm ld b, #8 flipLoopH: ld a, (hl) inc hl // Fast byte Fliping routine by John Metcaff // 7 6 5 4 3 2 1 0 Bit start position // <1 <3 3> 1> <1 <3 3> 1> Direction and number of shift // 0 1 2 3 4 5 6 7 Bit end position // 17 bytes / 66 cycles ld c, a // A = 76543210 | C = 76543210 rlca // A = 65432107 | C = 76543210 rlca // A = 54321076 | C = 76543210 xor c // A = 54321076 | C = 76543210 | in A XOR bit to keep 5x3x1x7x don't care of others because of AND 0xAA :) and #0xAA // A = 5_3_1_7_ | C = 76543210 | mask bit in A to be carryed over from C _6_4_2_0 xor c // A = 56341270 | C = 76543210 | restore back in A bit 5x3x1x7x and carry over _6_4_2_0 ld c, a // A = 56341270 | C = 56341270 rlca // A = 63412705 | C = 56341270 rlca // A = 34127056 | C = 56341270 rlca // A = 41270563 | C = 56341270 rrc c // A = 41270563 | C = 05634127 | rotate right C so bit 0 and 7 are at the right place for the XOR xor c // A = 41270563 | C = 05634127 | in A XOR bit to keep x12xx56x don't care of others because of AND 0x66 :) and #0x66 // A = _12__56_ | C = 05634127 | mask bit in A to be carryed over from C 0__34__7 xor c // A = 01234567 | C = 05634127 | restore back in A bit x12xx56x and carry over 0__34__7 ld (de), a inc de djnz flipLoopH ret __endasm; } #endif #if (SPRITEFX_USE_16x16) //----------------------------------------------------------------------------- // Horizontally flip 16x16 sprite void SpriteFX_FlipHorizontal16(const u8* src, u8* dest) __NAKED __PRESERVES(iyl, iyh) { src; // HL dest; // DE __asm ld bc, #16 add hl, bc ld b, #16 flipLoopH1: ld a, (hl) inc hl // Fast byte Fliping routine by John Metcaff ld c, a // a = 76543210 rlca rlca // a = 54321076 xor c and #0xAA xor c // a = 56341270 ld c, a rlca rlca rlca // a = 41270563 rrc c // c = 05634127 xor c and #0x66 xor c // a = 01234567 ld (de), a inc de djnz flipLoopH1 ld bc, #-32 add hl, bc ld b, #16 flipLoopH2: ld a, (hl) inc hl // Fast byte Fliping routine by John Metcaff ld c, a // a = 76543210 rlca rlca // a = 54321076 xor c and #0xAA xor c // a = 56341270 ld c, a rlca rlca rlca // a = 41270563 rrc c // c = 05634127 xor c and #0x66 xor c // a = 01234567 ld (de), a inc de djnz flipLoopH2 ret __endasm; } #endif #endif // (SPRITEFX_USE_FLIP) //----------------------------------------------------------------------------- // Masking //----------------------------------------------------------------------------- #if (SPRITEFX_USE_MASK) #if (SPRITEFX_USE_8x8) //----------------------------------------------------------------------------- // Mask 8x8 sprite void SpriteFX_Mask8(const u8* src, u8* dest, const u8* mask) __NAKED { src; // HL dest; // DE mask; // SP[2] -> IY __asm pop bc // Return address pop iy // Mask address push bc ld b, #8 maskLoop8: ld a, (hl) inc hl and 0(iy) inc iy ld (de), a inc de djnz maskLoop8 ret __endasm; } #endif #if (SPRITEFX_USE_16x16) //----------------------------------------------------------------------------- // Mask 16x16 sprite void SpriteFX_Mask16(const u8* src, u8* dest, const u8* mask) __NAKED { src; // HL dest; // DE mask; // SP[2] -> IY __asm pop bc // Return address pop iy // Mask address push bc ld b, #32 maskLoop16: ld a, (hl) and 0(iy) ld (de), a inc hl inc de inc iy djnz maskLoop16 ret __endasm; } #endif #endif // (SPRITEFX_USE_MASK) //----------------------------------------------------------------------------- // Rotating //----------------------------------------------------------------------------- #if (SPRITEFX_USE_ROTATE) //----------------------------------------------------------------------------- // Rotate 90° to right 8x8 sprite void SpriteFX_RotateRight8(const u8* src, u8* dest) __NAKED __PRESERVES(iyl, iyh) { src; // HL -> DE dest; // DE -> HL __asm ex de, hl ld c, #8 rotOuterLoopR: ld a, (de) inc de push hl ld b, #8 rotInnerLoopR: rla rr (hl) inc hl djnz rotInnerLoopR pop hl dec c jp nz, rotOuterLoopR ret __endasm; } #if (SPRITEFX_USE_16x16) //----------------------------------------------------------------------------- // Rotate 90° to right 16x16 sprite void SpriteFX_RotateRight16(const u8* src, u8* dest) { SpriteFX_RotateRight8(src, dest + 16); SpriteFX_RotateRight8(src + 8, dest); SpriteFX_RotateRight8(src + 16, dest + 24); SpriteFX_RotateRight8(src + 24, dest + 8); } #endif //----------------------------------------------------------------------------- // Rotate 90° to left 8x8 sprite void SpriteFX_RotateLeft8(const u8* src, u8* dest) __NAKED __PRESERVES(iyl, iyh) { src; // HL -> DE dest; // DE -> HL __asm ex de, hl ld c, #8 rotOuterloopL: ld a, (de) inc de push hl ld b, #8 rotInnerLoopL: rra rl (hl) inc hl djnz rotInnerLoopL pop hl dec c jp nz, rotOuterloopL ret __endasm; } #if (SPRITEFX_USE_16x16) //----------------------------------------------------------------------------- // Rotate 90° to left 16x16 sprite void SpriteFX_RotateLeft16(const u8* src, u8* dest) { SpriteFX_RotateLeft8(src, dest + 8); SpriteFX_RotateLeft8(src + 8, dest + 24); SpriteFX_RotateLeft8(src + 16, dest); SpriteFX_RotateLeft8(src + 24, dest + 16); } #endif //----------------------------------------------------------------------------- // Rotate 180° 8x8 sprite void SpriteFX_RotateHalfTurn8(const u8* src, u8* dest) __NAKED __PRESERVES(iyl, iyh) { src; // HL dest; // DE __asm ld bc, #7 add hl, bc ld b, #8 rotLoop180: ld a, (hl) dec hl // Fast byte Fliping routine by John Metcaff ld c, a // a = 76543210 rlca rlca // a = 54321076 xor c and #0xAA xor c // a = 56341270 ld c, a rlca rlca rlca // a = 41270563 rrc c // c = 05634127 xor c and #0x66 xor c // a = 01234567 ld (de), a inc de djnz rotLoop180 ret __endasm; } #if (SPRITEFX_USE_16x16) //----------------------------------------------------------------------------- // Rotate 180° 16x16 sprite void SpriteFX_RotateHalfTurn16(const u8* src, u8* dest) { SpriteFX_RotateHalfTurn8(src, dest + 24); SpriteFX_RotateHalfTurn8(src + 8, dest + 16); SpriteFX_RotateHalfTurn8(src + 16, dest + 8); SpriteFX_RotateHalfTurn8(src + 24, dest); } #endif #endif // (SPRITEFX_USE_ROTATE)