Search code examples
animationassemblyx86-16

How to set boundary for moving something around the screen when user presses keys, in assembly?


Say I have a code that makes a dot move around in the ES (extra seg) with the wasd keys. How do I check if the dot is at the first line and if so, it doesn't move?
(I asked my teacher if I need to use cmp and he said no)

In the proc of the up motion; (I took BX and set it to 0 to check if SI is also 0, then I just made a loop that checks if they're equal until BX is no longer on the first row). When I ran it, it didn't respond when I pressed w.

proc up
    mov ah, 0
    mov al, ' '
    mov [es:si], ax
    
    sub si, 80*2
    mov ah, 156
    mov al, '*'
    mov [es:si], ax
  ret
endp up

I tried to use cmp

proc up
    mov ah, 0
    mov al, ' '
    mov [es:si], ax
    
        mov bx, 0
check:
    cmp bx, 80*2
    jz move
    cmp si, bx
    jz not_move
    inc bx
    jmp check
move:
    sub si, 80*2
    mov ah, 156
    mov al, '*'
    mov [es:si], ax
not_move:
  ret
endp up

Solution

  •    mov bx, 0
    check:            <<< does not need a loop
       cmp bx, 80*2
       jz move
       cmp si, bx
       jz not_move    <<< needs redrawing because of unconditional wipe
       inc bx         <<< faster with `add bx,2`
       jmp check
    

    Any character that could exist in the top row of the 80x25 text screen will necessarily be located at one of the even addresses in the list {0, 2, 4, ... 158}.
    What your check loop currently is doing is verifying all the addresses in the top row, even the odd ones that won't ever match with SI.
    But why even have a loop? Just compare SI to 160 and if SI has a value of 160 or more then you know that the asterisk is not yet in the first row, and so you can safely move up.

    What follows is the corrected version of your up proc. If the code finds that the asterisk is in the first row then either not touch the screen at all (best) or, after wiping, redraw first instead of just returning!

    proc up
        cmp si, 160
        jb  not_move
        mov ax, 00h << 8 + ' '   ; BlackOnBlack space
        mov [es:si], ax
        sub si, 160
        mov ax, 9Ch << 8 + '*'   ; LightRedOnLightBlue asterisk
        mov [es:si], ax
    not_move:
        ret
    endp up
    

    (I asked my teacher if I need to use cmp and he said no)

    He was right, because instead of a cmp we could just try to subtract 160 from the dot's current address and check if the sub instruction produced a borrow, which it would do for all the addresses in the top row since they are in the list {0, 2, 4, ... 158}. If a borrow exists we would not move and maintain SI as it is:

    proc up
        mov ax, si               ; Keeps SI unmodified in case of 'not_move'
        sub ax, 160
        jb  not_move
        mov ax, 00h << 8 + ' '   ; BlackOnBlack space
        mov [es:si], ax
        sub si, 160
        mov ax, 9Ch << 8 + '*'   ; LightRedOnLightBlue asterisk
        mov [es:si], ax
    not_move:
        ret
    endp up