我应该如何在8086 TASM中全面移动图形对象?

时间:2018-05-15 19:36:38

标签: assembly dos x86-16 tasm

我试图在8086程序集中移动一个6x6像素的对象,但没有运气。 我希望它每秒移动4个像素,但目前它根本没有移动。

如果有人可以帮助我,我会很高兴,因为这是我高中计算机学习决赛的一个重要项目,我一直到周末结束

这是代码本身

    IDEAL
    MODEL medium

        DATASEG

    STACK 100h

    segment extra para public use16 ;Create Extra Segment that is 16 bits;
      ;(Removed the data segment for your sake, its very long pixel lists)

    CODESEG 

    ; -------------------------------
    ; change screen mode to text mode
    ; -------------------------------

    proc DrawPix
    ContLines:
        mov  cx, [LineLength]     ; How many times should the "rep movsb" should iterate
        push di         ; Save in stack the calculated position indicating the begining of the current line

        rep  movsb      ; copies bytes from si(image) to di (screen)

        pop  di         ; di was changed during the movesb. I change it back to pint to the begin of the line
        add  di, ScreenWidth ; by adding ScreenWidth (320)' I am moving the di to point to the next line
        dec  [NumOfLines]   ; Check if I wrote all the lines of the image
        jnz  ContLines    ; IF there are still pixcells to print on the screen(image not ended), loop to next line
        ret
        endp



    proc keycontrol
;check for arrow keys, and go to check arrow removed;
            jmp Shoot
    checkarrow:
        removed, irrelevant (supposed to change direction of turret)
    Exit:
    ret

    endp

    proc Shoot
    cmp [turretstate],1 ;turret facing up
    je bulletup
    cmp [turretstate],2;turret facing down
    je bulletdown
    cmp [turretstate],3;turret facing left
    je bulletleft
    cmp [turretstate],4;turret facing right
    je bulletright
    ret

    bulletup:
      not relevant for testing left movement
    bulletdown:
       not relevant for testing left movement
    bulletright:
    not relevant for testing left movement
    bulletleft:
        mov di,320*87+133
       lea si, [bullet]
       mov [LineLength],6
       mov [numOFLines],6
       call DrawPix
       ret
       endp

       proc leftbull
       pop dx
       dec dx
       mov di,320*84
       add di,dx
       lea si, [bullet]
       mov [LineLength],6
       mov [NumOfLines],6
       call DrawPix
       ret
       endp

       proc progbullets
       mov dx,0
       checkleft:
       inc dx
       cmp dx,136
       je exit1
       mov cx,87
       mov ah,0Dh
       int 10h
       cmp al,4
       jne checkleft
       push dx
       call leftbull
       exit1:
       ret
       endp
    ;---------------------------------------------;
    ;---------------------------------------------;
    start:


        Main_Loop:
        mov [sectimer], 0
        mov ah,00H
        int 1Ah
        mov [sectimer],dx
        call keycontrol
        func_Loop:
        call progbullets
        mov ah,00H
        int 1Ah
        sub dx,18
        cmp dx,[sectimer]
        jge Main_Loop
        ;call moveGame
        jmp func_Loop
        ; call the operating system to terminate this program

        mov ah,1
        int 21h
        mov ax, 4C00H
        int 21h  


    ends
    end start

该程序有一个起始屏幕,可以移动炮塔,并放置“子弹”,但我无法让它们移动,它也有一些奇怪的像素。 拍摄过程是主要问题,它应该在x值上一个接一个(子弹保持在相同的y值),并检查像素是否为红色(子弹颜色),如果是这样的话它还剩下一个。

编辑:非常感谢您的帮助,我是新来的,真的需要它。我没有适合gui tasm的调试器,这是我正在使用的系统,所以我很难做到这一点 我删除了大部分不必要的东西,但不确定还有什么不必要,因为我不确定什么是不起作用的东西。我猜“射击”过程是最相关的。拍摄仅适用于左侧子弹,因为我想先测试它,但它不适用于此

1 个答案:

答案 0 :(得分:2)

缩短代码的主要问题之一是:

   push dx
   call leftbull

...

   proc leftbull
   pop dx
   dec dx

您错过了callret说明的实施细节。它们也使用“堆栈”存储区域,因此pop dx不会在push dx之前读取call存储到堆栈中的值,而是“返回地址”,即地址call指令后的下一条指令。

您可以使用您自己的子程序调用约定来修复这样的问题,您可以在寄存器中传递参数,例如在这种情况下只删除push dxpop dx,因为您已经拥有所需的dx中的值(但请验证是否也从其他地方调用了leftbull,以修复该调用以将参数放入dx)。

另外一点是,搜索现有子弹的像素数据有点天真和原始的方法。而是在数据段中为子弹位置保留一些空间(取决于您可以同时在屏幕上显示多少个子弹),并继续更新这些位置,这样您就不需要通过像素数据搜索当前位置。 / p>

另外,对于更复杂的游戏,您可以将一些精灵相互绘制,因此在这种情况下搜索像素数据会失败,因为精灵“在”下面是不可见的=未找到。但读取视频内存的速度也非常慢,因此扫描320像素的速度远远低于扫描数据段中的320字节。通过int 10h服务读取像素值通常比直接从视频内存中读取它们要慢十倍,因此您当前的想法比使用子弹位置“变量”慢约200-1000倍,并通过这些更新子弹

重新考虑你的整个游戏原理图和主循环算法,或者尝试在纸上画一段时间,你真正需要存储什么,以保持完整的“世界状态”,让你从头开始重绘世界框架(有点跟随现代MVC SW架构模式),还有那些不同的上/下/左/右代码变体...尝试对其进行参数化,因此您可以使用单个通用例程处理所有情况,根据以下内容执行不同的操作特定子弹的参数。

我为一场代码高尔夫比赛做过一次2048字节长的DOS游戏,所以代码并不是最可读的,但你可能还是快速看一下,也许它会激发你一两件事(来源是包括在内,当时我正在使用TASM):http://www.pouet.net/prod.php?which=2692