Search code examples
assemblygraphicsspritex86-16tasm

TASM sprite looks weird


I have recently begun coding in TASM and am currently working on sprites. In my program, I receive a sprite and print it on screen (Graphic mode).

The program works fine essentially but the numbers that are printed look weird. Pixels that are supposed to be right beneath each other appear somewhat distant.

This is how I call the function:

push 10 ; amount of lines
push 10 ; length of each line
push 4 ; color
push 100 ; y
push 160 ; x
push offset card2
call Sprite_Figure

And here is the function:

proc Sprite_Figure
push bp
mov bp, sp
push ax
push bx
push cx
push dx
push si
    
    mov si, [bp+4] ; sprite offset
    mov cx, [bp+14] ; number of lines   
    loop1:
        mov dx, [bp+12] ; size of one line
        loop2:
            push dx
            xor dx, dx
            cmp [byte ptr si], 0
            je continue
            
            print:
            mov bx, [bp+8] ; current row    
            
            mov ax, 320
            mul bx
            mov bx, ax ; calculation to get location of pixel on screen
            add bx, [bp+6] ; x
            
            mov ax, [bp+10] ; color
            mov [es:bx], al ; color location, es val is 0A000h
            
            continue:
            pop dx
            inc si ; next element in array  
            dec dx
            inc [bp+6] ; right by one
            
            cmp dx, 0
            jne loop2
        
        mov ax, 320
        sub ax, [bp+12] ; the size of one line
        add [bp+6], ax ; new line
        inc [bp+8] ; one line down  
        dec cx
        cmp cx, 0
        jne loop1
pop si
pop dx
pop cx
pop bx
pop ax
pop bp
ret 12
endp Sprite_Figure  

This is how the sprite looks on screen:
enter image description here

Does anyone have any ideas?


Solution

  • The code is mixing 2 ways of moving down a line! This explains why the code descends at double rate on the screen, the value 320 gets added twice.

    Basically you have a couple of ways to draw your character matrix:

    • A pair of nested loops where you vary the X and Y coordinates and calculate the address on screen each time.
    • A pair of nested loops where you calculate once the address of the upperleftcorner pixel on screen, and then change that address as needed.

    The line

    inc [bp+8]      ; one line down
    

    suits the 1st method, whereas the lines

    mov ax, 320
    sub ax, [bp+12] ; the size of one line
    add [bp+6], ax  ; new line
    

    suit the 2nd method.

    Method1

    Because the inner loop has repeatedly incremented the X coordinate, for restoration purposes, you have to subtract the total number of increments:

        ...
        dec dx
        jnz loop2
        mov ax, [bp+12] ; Size of one line (number of increments)
        sub [bp+6], ax  ; X is now restored
        inc [bp+8]      ; Y+ one line down  
        dec cx
        jnz loop1
    

    Method2

    The coordinates are used just the one time to obtain an address in BX. Only the address varies after that. A lot less instructions in the loops, means this is a much more efficient approach.

        mov ax, 320
        mul [word ptr bp+8] ; Y
        mov bx, ax
        add bx, [bp+6]      ; X
    
        mov si, [bp+4]      ; sprite offset
        mov cx, [bp+14]     ; number of lines   
        mov al, [bp+10]     ; color
    
    loop1:
        mov dx, [bp+12]     ; size of one line
    
    loop2:
        cmp [byte ptr si], 0
        je continue
        mov [es:bx], al     ; ES val is 0A000h
    continue:
        inc si              ; next byte in character pattern
        inc bx              ; X+ right by one on screen
        dec dx
        jnz loop2
    
        sub bx, [bp+12]     ; Address of left side
        add bx, 320         ; Address of new line
        dec cx
        jnz loop1