我正在为univesity工作,我们需要创建一个简单的breakout / arkanoid克隆,它很顺利但是我发现了一个错误会删除屏幕上的所有内容,这个bug是随机的,但我怀疑它与我的DrawPaddle功能.也许你可以发现错误或了解为什么视频内存会这样做.

游戏必须使用16位ms-dos程序集完成,我使用NASM VAL DosBox创建它,我用以下代码编译它:

nasm -f obj test.asm
val test.obj

游戏只是使用键盘箭头在固定屏幕上移动球拍,/uploads/20230904/6ebd25111731ce728b03cad3cbed6b05.png,当程序溢出时它看起来像这样:http://puu.sh/yeKEy/caeef089d1.png或http://puu.sh/yeKJH/1106e1e823.png

我注意到奇怪的行为只发生在我移动桨时它会随机发生,例如现在我从程序中删除了几乎所有其他东西,它可能需要几次尝试来获取bug.

这是DrawPaddle代码:

DrawPaddle:
    push di
    mov di,[paddleposition]
    mov cx,5 ;the paddle will be 5 pixels tall
.p0:
    push cx
    mov cx,paddlesize
.p1:
    mov byte [es:di],bl
    inc di
    loop .p1
    add di,screenweight - paddlesize
    pop cx
    loop .p0
    pop di
    ret

这是完整的代码,它使用键盘处理程序读取输入,并使用320x200x256直接写入视频内存.

BITS 16

stacksize       EQU 0200h

;Constantes
;Direccion de inicio de la memoria de video
videobase       EQU 0a000h

;Definicion de colores
black           EQU 0
green           EQU 00110000b

;Screen data
screenweight    EQU 320


;Paddle data
startx      EQU 140
starty      EQU 170
paddlesize      EQU 40
paddlecolor     EQU 00101010b 

;Paddle movement limits
leftlimit       EQU starty * screenweight + 1 + 10 + 1
rightlimit       EQU ((starty + 1) * screenweight) - paddlesize - 10 - 1

segment mystack stack
    resb stacksize
stacktop:   

segment mydata data

;Variables
escpressed  dw 0
leftpressed     dw 0
rightpressed    dw 0
oldintseg       resw 1
oldintoff       resw 1
originalVideoMode resb 1
paddleposition  resw 1

segment mycode code
;Subrutinas

KeybInt:
        push    ds ;guardamos ds:ax       
        push    ax              

        mov ax,mydata ;los re-inicializamos
        mov ds,ax          

        cli

.getstatus:
        in      al,64h
        test    al,02h
        loopnz  .getstatus ;esperando a que el puerto esté listo

        in      al,60h ;obtenemos el codigo make o break de la tecla leida

        cmp     al,01h ;revisamos si es escape
        jne     .revEsc 
        mov     word [escpressed],1
        jmp     .kbread
.revEsc:
        cmp     al,81h ;revisamos si el escape fue soltado
        jne     .revIzq
        mov     word [escpressed],0
        jmp     .kbread
.revIzq:
        cmp     al,4bh ;revisamos si es la flecha izquierda
        jne     .revDer
        mov     word [leftpressed],1
        jmp     .kbread
.revDer:
        cmp     al,4dh ;revisamos si es la flecha derecha
        jne     .revIzq2
        mov     word [rightpressed],1
        jmp     .kbread
.revIzq2:
        cmp     al,0cbh ;si se solto la flecha izquierda
        jne     .revDer2
        mov     word [leftpressed],0
        jmp     .kbread
.revDer2:
        cmp     al,0cdh ;o la derecha
        jne     .kbread
        mov     word [rightpressed],0
        jmp     .kbread
.kbread:
        in      al,61h     
        or      al,10000000b
        out     61h,al            
        and     al,01111111b                     
        out     61h,al                      
        mov     al,20h
        out     20h,al               

        sti 

        pop     ax ;recuperamos ds:ax       
        pop     ds
        iret

DrawStage:
    push di
    push bx
    ;movemos el cursor a la posicion 10,10
    ;que seria en realidad 10*320+10
    mov di,(10 * screenweight) + 10
    ;ahora repetiremos esto 320-20 veces
    mov cx,300
.h1:
    mov byte [es:di],green
    inc di
    loop .h1

    mov di,(190 * screenweight) + 10
    ;ahora repetiremos esto 320-20 veces
    mov cx,301
.h2:
    mov byte [es:di],green
    inc di
    loop .h2    

    ;ahora volveremos al primer punto
    ;y dibujaremos hacia abajo
    mov di,(10 * screenweight) + 10
    ;y lo repetiremos 200-20 veces
    mov cx,180
.v1:
    mov byte [es:di],green
    add di,screenweight
    loop .v1

    mov di,(10 * screenweight) + 310
    mov cx,180
.v2:
    mov byte [es:di],screenweight
    loop .v2

    pop bx
    pop di
    ret

;Rutina para dibujar el palo
;Recibe en bl el color del mismo
DrawPaddle:
    push di
    mov di,screenweight - paddlesize
    pop cx
    loop .p0
    pop di
    ret

Delay1:
    mov dx,4
    sub dx,3
.pause1:
    mov cx,6000
.pause2:
    dec cx
    jne .pause2
    dec dx
    jne .pause1
    ret

..start:
    mov ax,mydata
    mov ds,ax
    mov ax,mystack
    mov ss,ax
    mov sp,stacktop

    ;guardando el manejador actual
    mov ah,35h
    mov al,9h
    int 21h
    mov [oldintseg],es
    mov [oldintoff],bx

    ;instalando el manejador nuevo
    mov ax,mycode
    mov es,ax
    mov dx,KeybInt
    mov ax,cs
    mov ds,ax
    mov ah,25h
    mov al,9h
    int 21h

    ;restaurando el segmento de datos
    mov ax,ax

    ;guardando el modo de video y aplicando el nuevo
    xor ax,0fh
    int 10h
    mov [originalVideoMode],al
    mov ah,00h
    mov al,13h
    int 10h
    ;coordenada de inicio para el palo
    mov ax,(screenweight * starty) + startx
    mov word [paddleposition],videobase
    mov es,ax

    call DrawStage
    mov bl,paddlecolor
    call DrawPaddle
    jmp .main

.main:
    call Delay1

    ;leemos las enTradas
    cmp word [escpressed],1
    je .dosexit
    cmp word [rightpressed],1
    je .movRight
    cmp word [leftpressed],1
    je .movLeft
    jmp .main
.movRight:
    mov bl,black
    call DrawPaddle
    cmp word [paddleposition],rightlimit
    je .ending
    inc word [paddleposition]
    jmp .ending
.movLeft:
    mov bl,leftlimit
    je .ending
    dec word [paddleposition]
    jmp .ending
.ending:    
    mov bl,paddlecolor
    call DrawPaddle
    jmp .main

.dosexit:
    ;restaurando el modo de video original
    mov ah,00h
    mov byte al,[originalVideoMode]
    int 10h

    ;restaurando el manejador de teclado original
    mov dx,[oldintoff]
    mov ax,[oldintseg]
    mov ds,9h
    int 21h
    mov al,0
    mov ah,4ch
    int 21h

谢谢阅读!

您可以在键盘中断中修改cx而不保留它.

^^^这是答案(导致你的错误的原因),而不仅仅是一些建议

这里有一些建议如下:

在中断中有任何循环(动态延迟)也是错误的,中断应该尽可能快地进行.

我不记得从头部读取键盘的0x6X端口的正确方法(我只记得它有点棘手,让它完全正确),所以我不打算检查特定的输入/输出序列及其正确性.

但是如果你将在实际当前状态中设置XXXpressed中断,并且主循环将太慢,则可能看不到非常短的按键(因为输入没有被缓冲).对于像arkanoid clone一样的简单游戏,这是可以的,我根本不会被这个打扰,听起来像是正确的行为(你需要实际上非常快地把钥匙保持得这么短).

您还可以通过在中断代码处理程序附近保留一些数据空间(在iret之后将escpressed dw 0移动到代码部分中)来避免中断中的ds设置,然后将其用作mov word [cs:escpressed],1等.如果您实际上以更有效的方式设置内存标志和短中断代码(可以简化很多),那么使用cs的代价:内部中断寻址将低于ds设置.

你有多广泛地将slow loop instruction用于所有主循环,但是在延迟子程序中你做了更快的dec cx jnz …替代方案.

我确实检查了如何编写DOS键盘处理程序,所以这是我的建议(不幸的是我没有测试它,如果它工作):

segment mycode code

escpressed      db 0
leftpressed     db 0
rightpressed    db 0

KeybInt:
        cli
        push    ax      ;guardamos ax

        ; when IRQ1 is fired,int 9 is called to handle it and the input
        ; already waits on port 0x60,no need to validate IBF flag on 0x64

        in      al,60h ;obtenemos el codigo make o break de la tecla leida
        mov     ah,al
        and     al,0x7F ; AL = scan code without pressed/released flag
        shr     ah,7
        xor     ah,1    ; AH = 1/0 pressed/released

        cmp     al,01h ;revisamos si es escape
        jne     .checkLeft
        mov     [cs:escpressed],ah
        jmp     .kbread
.checkLeft:
        cmp     al,4bh ;revisamos si es la flecha izquierda
        jne     .checkRight
        mov     [cs:leftpressed],ah
        jmp     .kbread
.checkRight:
        cmp     al,4dh ;revisamos si es la flecha derecha
        jne     .kbread
        mov     [cs:rightpressed],ah
.kbread:

        in      al,61h
        mov     ah,al          ; store original value
        or      al,al         ; set "enable kbd" bit
        mov     al,ah
        out     61h,al         ; set original value back

        mov     al,al         ; send end-of-interrupt signal to 8259 IC

        pop     ax ;recuperamos ax
        sti        ; not needed in real x86 real mode,IRET restores flags
        iret       ; but explicit STI paired with CLI may help some VMs

…然后在游戏代码中,要检查密钥的状态,你也必须使用cs:

...
        cmp     byte [cs:escpressed],1
        ...

程序集 – 使用自己的键盘中断`int 09h`处理程序时代码的奇怪行为(损坏的绘制)的更多相关文章

  1. 如何在Swift中断后恢复音频?

    我按照指示here,我已经把这个测试项目放在一起来处理音频播放的中断.具体来说,我正在使用默认iphone时钟应用程序中的警报作为中断.似乎中断处理程序被调用,但没有超过let=interruptionType行,因为“错误类型”出现了两次.此外,按照一个想法here,我已经修改了处理程序结果是所有“开始”,“音频暂停”,“结束”和“音频恢复”都显示在控制台中,但音频播放实际上并未恢复.注意:我将

  2. Windows中的中断处理

    我想知道哪些线程处理设备中断.当用户模式线程运行时出现中断时会发生什么?本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如发现本站有涉嫌侵权/违法违规的内容,请发送邮件至dio@foxmail.com举报,一经查实,本站将立刻删除。

  3. ubuntu – 高LOC中断的原因是什么?

    我看到LOC中断出现了巨大的峰值–大约每秒400万,究竟是什么会导致LOC中断,这会导致这些峰值,我该怎么办呢?

  4. 如何将单个换行符作为PHP Markdown中的实线中断处理?

    我正在读http://github.github.com/github-flavored-markdown/我想在PHPMarkdown中实现“Newline修改”:最好我想到的是:但这是非常不可预知的.在您的降价文件中查找行:并将其下面的preg模式从:至:或者你可以扩展markdown类,重新声明’doHardBreaks’功能,并将返回值改为上面的代码问候,Achmad

  5. php – MySQL中“许多连续中断的连接请求”是什么?

    我经常有以下错误:PHP致命错误:带有消息’sqlSTATE[HY000]的未捕获异常’PDOException'[1129]由于许多连接错误,主机’MYSERVER’被阻止;使用’MysqLadminflush-hosts’取消阻止使用常规MysqLadminflush-hosts命令或增加max_connect_errors系统变量很容易解决问题,如here所示.但是!什么是“许多连续中断的连接请求”,为什么会发生这种情况?

  6. Windows Server 2008,一个非常小的DDoS能够杀死我的服务器

    4个所以我有运行WindowsServer2008的i73.2Ghz6核专用服务器,网速为1Gbps.一切都运行良好,机器强大到足以处理甚至高达700Mbps的DoS攻击非常容易..没有滞后,没有问题..有时候我们习惯了大攻击,但一次只有1或2个IP,所以cpu使用率总是很好,直到最近,我们发现了一个全新的DDoS攻击!当我的服务器能够在以前的网络使用率的90%之前保持活力时,他怎么能以4%的网络使用率杀死我的服务器?!

  7. windows-xp – 什么是“中断”过程以及为什么它如此喜欢我的CPU?

    我在IntelCoreDuo2GHz上安装了WindowsXPSP3.根据ProcessExplorer,“Interrupts”进程不断占用30-40%的cpu.这是正常的吗?

  8. windows – 重新安装操作系统时软件raid是否中断?

    重新安装操作系统时,软件raid会中断吗?如果是,有没有办法防止它,或重新安装后没有数据丢失重新创建它?如果不是,那么在做这件事时你有什么需要记住的吗?还是只是自动修复?

  9. 程序集 – 使用自己的键盘中断`int 09h`处理程序时代码的奇怪行为(损坏的绘制)

    替代方案.我确实检查了如何编写DOS键盘处理程序,所以这是我的建议:…然后在游戏代码中,要检查密钥的状态,你也必须使用cs:

  10. ios – AVAudioRecorder在音频会话中断结束后不在后台录制

    int’.错误代码列在AVAudioSession.h文件中:

随机推荐

  1. static – 在页面之间共享数据的最佳实践

    我想知道在UWP的页面之间发送像’selectedItem’等变量的最佳做法是什么?创建一个每个页面都知道的静态全局变量类是一个好主意吗?

  2. .net – 为Windows窗体控件提供百分比宽度/高度

    WindowsForm开发的新手,但在Web开发方面经验丰富.有没有办法为Windows窗体控件指定百分比宽度/高度,以便在用户调整窗口大小时扩展/缩小?当窗口调整大小时,可以编写代码来改变控件的宽度/高度,但我希望有更好的方法,比如在HTML/CSS中.在那儿?

  3. 使用Windows Azure查询表存储数据

    我需要使用特定帐户吗?>将应用程序部署到Azure服务后,如何查询数据?GoogleAppEngine有一个数据查看器/查询工具,Azure有类似的东西吗?>您可以看到的sqlExpressintance仅在开发结构中,并且一旦您表示没有等效,所以请小心使用它.>您可以尝试使用Linqpad查询表格.看看JamieThomson的thispost.

  4. windows – SetupDiGetClassDevs是否与文档中的设备实例ID一起使用?

    有没有更好的方法可以使用DBT_DEVICEARRIVAL事件中的数据获取设备的更多信息?您似乎必须指定DIGCF_ALLCLASSES标志以查找与给定设备实例ID匹配的所有类,或者指定ClassGuid并使用DIGCF_DEFAULT标志.这对我有用:带输出:

  5. Windows Live ID是OpenID提供商吗?

    不,WindowsLiveID不是OpenID提供商.他们使用专有协议.自从他们的“测试版”期结束以来,他们从未宣布计划继续它.

  6. 如果我在代码中进行了更改,是否需要重新安装Windows服务?

    我写了一个Windows服务并安装它.现在我对代码进行了一些更改并重新构建了解决方案.我还应该重新安装服务吗?不,只需停止它,替换文件,然后重新启动它.

  7. 带有双引号的字符串回显使用Windows批处理输出文件

    我正在尝试使用Windows批处理文件重写配置文件.我循环遍历文件的行并查找我想要用指定的新行替换的行.我有一个’函数’将行写入文件问题是%Text%是一个嵌入双引号的字符串.然后失败了.可能还有其他角色也会导致失败.如何才能使用配置文件中的所有文本?尝试将所有“在文本中替换为^”.^是转义字符,因此“将被视为常规字符你可以尝试以下方法:其他可能导致错误的字符是:

  8. .net – 将控制台应用程序转换为服务?

    我正在寻找不同的优势/劣势,将我们长期使用的控制台应用程序转换为Windows服务.我们为ActiveMQ使用了一个叫做java服务包装器的东西,我相信人们告诉我你可以用它包装任何东西.这并不是说你应该用它包装任何东西;我们遇到了这个问题.控制台应用程序是一个.NET控制台应用程序,默认情况下会将大量信息记录到控制台,尽管这是可配置的.任何推荐?我们应该在VisualStudio中将其重建为服务吗?我使用“-install”/“-uninstall”开关执行此操作.例如,seehere.

  9. windows – 捕获外部程序的STDOUT和STDERR *同时*它正在执行(Ruby)

    哦,我在Windows上:-(实际上,它比我想象的要简单,这看起来很完美:…是的,它适用于Windows!

  10. windows – 当我试图批量打印变量时,为什么我得到“Echo is on”

    我想要执行一个简单的批处理文件脚本:当我在XP中运行时,它给了我预期的输出,但是当我在Vista或Windows7中运行它时,我在尝试打印值时得到“EchoisOn”.以下是程序的输出:摆脱集合表达式中的空格.等号(=)的两侧可以并且应该没有空格BTW:我通常在@echo关闭的情况下启动所有批处理文件,并以@echo结束它们,所以我可以避免将代码与批处理文件的输出混合.它只是使您的批处理文件输出更好,更清洁.

返回
顶部