;
; Scaled sprite plot
; ------------------
;
; Displays a rectangle of the supplied sprite data. Scales the display up
; or down independently in x and y axis. Magnification factors are fixed
; point fractional.
;   output_size = input_size / magnification
; Functions provided for colour depths of 8, 16, 32 bpp. Addresses need not
; be word aligned for 8bpp and 16bpp. The sprite must have the same colour
; depth as the display and the supplied sprite top left address should point
; to the sprite data, not the header. Does not work for negative mags.
;
; inputs, r0 = &blk
;   blk+0  -> r0 = x magnification
;   blk+4  -> r1 = y magnification
;   blk+8  -> r2 = dest rectangle x offset in pixels
;   blk+12 -> r3 = dest rectangle y offset
;   blk+16 -> r4 = dest rectangle width in pixels (x loop count)
;   blk+20 -> r5 = dest rectangle height (y loop count)
;   blk+24 -> r6 = dest rectangle top left address
;   blk+28 -> r7 = screen width in bytes
;   blk+32 -> r8 = sprite top left address
;   blk+36 -> r9 = sprite width in bytes
;
; dest offsets are relative to the display window.
;
; note. r5 is used for both x & y counts, (upper half for x)
;

frac  EQU 16 ; fractional bits in x/y magnification
split EQU 16 ; number of bits of r5 used for y count

    AREA |A$$code|, CODE, READONLY

    EXPORT ka_scale_raw_8bpp

; colour depth = 8bpp
ka_scale_raw_8bpp
    mov     ip, sp
    stmdb   sp!, {r0, r4-r9, fp, ip, lr, pc}
    sub     fp, ip, #4
    ldmia   r0, {r0-r9}             ; load registers from blk
    mul     r2, r0, r2              ; initial Xoffset
    mul     r3, r1, r3              ; initial Yoffset
    stmdb   sp!, {r2,r8,r9}
yloop8
    ldmfd   sp, {r2,r8,r9}          ; read fron stack
    sub     r5, r5, r4, asl #split  ; width count
    mov     r14, r3, asr #frac      ; (int)Yoffset
    mla     r14, r9, r14, r8        ; src_addr += src_width * (int)Yoffset
    mov     r12, r6                 ; dest line start
    tst     r12, #3                 ; check dest alignment
    beq     xloop8
; store up to 3 single pixels
xloop8_1
    ldrb    r9, [r14, r2, asr #frac]; pixel = *(src_addr + (int)Xoffset)
    add     r2, r2, r0              ; increment Xoffset
    strb    r9, [r12], #1           ; display pixel
    adds    r5, r5, #1<<split       ; decrement x count by 1 pixel
    bgt     xend8
    tst     r12, #3                 ; check dest alignment
    bne     xloop8_1
xloop8
    cmn     r5, #3<<split           ; check for a width less than 4
    bge     xloop8_1
; store a word of 4 pixels
    ldrb    r9, [r14, r2, asr #frac]; pixel = *(src_addr + (int)Xoffset)
    add     r2, r2, r0              ; increment Xoffset
    ldrb    r8, [r14, r2, asr #frac]; pixel = *(src_addr + (int)Xoffset)
    add     r2, r2, r0              ; increment Xoffset
    orr     r9, r9, r8, lsl #8
    ldrb    r8, [r14, r2, asr #frac]; pixel = *(src_addr + (int)Xoffset)
    add     r2, r2, r0              ; increment Xoffset
    orr     r9, r9, r8, lsl #16
    ldrb    r8, [r14, r2, asr #frac]; pixel = *(src_addr + (int)Xoffset)
    add     r2, r2, r0              ; increment Xoffset
    orr     r9, r9, r8, lsl #24
    str     r9, [r12], #4           ; display 4 pixels
    adds    r5, r5, #4<<split       ; decrement x count by 4 pixels
    ble     xloop8
xend8
    add     r3, r3, r1              ; increment Yoffset
    add     r6, r6, r7              ; next dest line start
    subs    r5, r5, #1              ; decrement y count by 1 pixel
    bgt     yloop8
    ldmdb   fp, {r4-r9, fp, sp, pc}

;-----------------------------------------------------------

    ALIGN 32
    EXPORT ka_scale_raw_16bpp
; colour depth = 15/16bpp
ka_scale_raw_16bpp
    mov     ip, sp
    stmdb   sp!, {r0, r4-r9, fp, ip, lr, pc}
    sub     fp, ip, #4
    ldmia   r0, {r0-r9}             ; load registers from blk
    bic     r6, r6, #1              ; ensure pixel alignment
    mul     r2, r0, r2              ; initial Xoffset
    mul     r3, r1, r3              ; initial Yoffset
    stmfd   sp!, {r2,r8,r9}
yloop16
    ldmfd   sp, {r2,r8,r9}          ; read fron stack
    sub     r5, r5, r4, asl #split  ; width count
    mov     r14, r3, asr #frac      ; (int)Yoffset
    mla     r14, r9, r14, r8        ; src_addr += src_width * (int)Yoffset
    mov     r12, r6                 ; dest line start
    tst     r12, #3                 ; check dest alignment
    beq     xloop16
; store a single pixel
xloop16_1
    mov     r9, r2, asr #frac       ; (int)Xoffset
    ldr     r9, [r14, r9, asl #1]   ; pixel = *(src_addr + (int)Xoffset(half words))
    strb    r9, [r12], #1           ; display left half of pixel
    add     r2, r2, r0              ; increment Xoffset
    mov     r9, r9, lsr #8
    strb    r9, [r12], #1           ; display right half of pixel
    adds    r5, r5, #1<<split       ; decrement x count by 1 pixel
    bgt     xend16
xloop16
    cmn     r5, #1<<split           ; check for a width less than 2
    bge     xloop16_1
; store a word of 2 pixels
    mov     r9, r2, asr #frac       ; (int)Xoffset
    ldr     r9, [r14, r9, asl #1]   ; pixel = *(src_addr + (int)Xoffset(half words))
    add     r2, r2, r0              ; increment Xoffset
    mov     r9, r9, lsl #16
    mov     r9, r9, lsr #16
    mov     r8, r2, asr #frac       ; (int)Xoffset
    ldr     r8, [r14, r8, asl #1]   ; pixel = *(src_addr + (int)Xoffset(half words))
    add     r2, r2, r0              ; increment Xoffset
    orr     r9, r9, r8, lsl #16
    str     r9, [r12], #4           ; display 2 pixels
    adds    r5, r5, #2<<split       ; decrement x count by 2 pixels
    ble     xloop16
xend16
    add     r3, r3, r1              ; increment Yoffset
    add     r6, r6, r7              ; next dest line start
    subs    r5, r5, #1              ; decrement y count by 1 pixel
    bgt     yloop16
    add     sp, sp, #12
    ldmdb   fp, {r4-r9, fp, sp, pc}

;-----------------------------------------------------------

    ALIGN 32
    EXPORT ka_scale_raw_16bpp_sh
; colour depth = 15/16bpp
ka_scale_raw_16bpp_sh
    mov     ip, sp
    stmdb   sp!, {r0, r4-r9, fp, ip, lr, pc}
    sub     fp, ip, #4
    ldmia   r0, {r0-r9}             ; load registers from blk
    bic     r6, r6, #1              ; ensure pixel alignment
    mul     r2, r0, r2              ; initial Xoffset
    mul     r3, r1, r3              ; initial Yoffset
    stmfd   sp!, {r2,r8,r9}
yloop16_sh
    ldmfd   sp, {r2,r8,r9}          ; read fron stack
    sub     r5, r5, r4, asl #split  ; width count
    mov     r14, r3, asr #frac      ; (int)Yoffset
    mla     r14, r9, r14, r8        ; src_addr += src_width * (int)Yoffset
    mov     r12, r6                 ; dest line start
    tst     r12, #3                 ; check dest alignment
    beq     xloop16_sh
; store a single pixel
xloop16_1_sh
    mov     r9, r2, asr #frac       ; (int)Xoffset
    mov     r9, r9, asl #1
    ldrh    r9, [r14, r9]           ; pixel = *(src_addr + (int)Xoffset(half words))
    add     r2, r2, r0              ; increment Xoffset
    strh    r9, [r12], #2           ; display pixel
    adds    r5, r5, #1<<split       ; decrement x count by 1 pixel
    bgt     xend16_sh
xloop16_sh
    cmn     r5, #1<<split           ; check for a width less than 2
    bge     xloop16_1_sh
; store a word of 2 pixels
    mov     r9, r2, asr #frac       ; (int)Xoffset
    mov     r9, r9, asl #1
    ldrh    r9, [r14, r9]           ; pixel = *(src_addr + (int)Xoffset(half words))
    add     r2, r2, r0              ; increment Xoffset
    mov     r8, r2, asr #frac       ; (int)Xoffset
    mov     r8, r8, asl #1
    ldrh    r8, [r14, r8]           ; pixel = *(src_addr + (int)Xoffset(half words))
    add     r2, r2, r0              ; increment Xoffset
    orr     r9, r9, r8, lsl #16
    str     r9, [r12], #4           ; display 2 pixels
    adds    r5, r5, #2<<split       ; decrement x count by 2 pixels
    ble     xloop16_sh
xend16_sh
    add     r3, r3, r1              ; increment Yoffset
    add     r6, r6, r7              ; next dest line start
    subs    r5, r5, #1              ; decrement y count by 1 pixel
    bgt     yloop16_sh
    add     sp, sp, #12
    ldmdb   fp, {r4-r9, fp, sp, pc}

;-----------------------------------------------------------

    ALIGN 32
    EXPORT ka_scale_raw_32bpp
; colour depth = 32bpp
ka_scale_raw_32bpp
    mov     ip, sp
    stmdb   sp!, {r0, r4-r9, fp, ip, lr, pc}
    sub     fp, ip, #4
    ldmia   r0, {r0-r9}             ; load registers from blk
    bic     r6, r6, #3              ; ensure pixel alignment
    mul     r2, r0, r2              ; initial Xoffset
    mul     r3, r1, r3              ; initial Yoffset
    stmfd   sp!, {r2,r8,r9}
yloop32
    ldmfd   sp, {r2,r8,r9}          ; read fron stack
    sub     r5, r5, r4, asl #split  ; width count
    mov     r14, r3, asr #frac      ; (int)Yoffset
    mla     r14, r9, r14, r8        ; src_addr += src_width * Yoffset
    mov     r12, r6                 ; dest line start
    ands    r9, r4, #3
    rsbne   r9, r9, #4
    mov     r8, #(xloop32_one - xloop32)
    mul     r9, r8, r9
    adr     r8, xloop32
    add     r8, r8, r9
    mov     pc, r8
xloop32
; note, do 4 pixels in line if possible to save a few cycles.
    mov     r9, r2, asr #frac       ; (int)Xoffset
    ldr     r9, [r14, r9, asl #2]   ; pixel = *(src_addr + (int)Xoffset(words))
    add     r2, r2, r0              ; increment Xoffset
    str     r9, [r12], #4           ; display pixel
xloop32_one
    mov     r8, r2, asr #frac       ; (int)Xoffset
    ldr     r8, [r14, r8, asl #2]   ; pixel = *(src_addr + (int)Xoffset(words))
    add     r2, r2, r0              ; increment Xoffset
    str     r8, [r12], #4           ; display pixel
    mov     r9, r2, asr #frac       ; (int)Xoffset
    ldr     r9, [r14, r9, asl #2]   ; pixel = *(src_addr + (int)Xoffset(words))
    add     r2, r2, r0              ; increment Xoffset
    str     r9, [r12], #4           ; display pixel
    mov     r8, r2, asr #frac       ; (int)Xoffset
    ldr     r8, [r14, r8, asl #2]   ; pixel = *(src_addr + (int)Xoffset(words))
    add     r2, r2, r0              ; increment Xoffset
    str     r8, [r12], #4           ; display pixel
    adds    r5, r5, #4<<split       ; decrement x count by 1 pixel
    blt     xloop32
xend32
    add     r3, r3, r1              ; increment Yoffset
    add     r6, r6, r7              ; next dest line start
    mov     r5,r5, lsl #split       ; get rid of any x count > 0
    mov     r5,r5, lsr #split
    subs    r5, r5, #1              ; decrement y count by 1 pixel
    bgt     yloop32
    add     sp, sp, #12
    ldmdb   fp, {r4-r9, fp, sp, pc}

;-----------------------------------------------------------
    ALIGN 32
    END
