热门标签 | HotTags
当前位置:  开发笔记 > 编程语言 > 正文

使用自己的键盘中断`int09h`处理程序时代码的奇怪行为(损坏的绘制)

如何解决《使用自己的键盘中断`int09h`处理程序时代码的奇怪行为(损坏的绘制)》经验,为你挑选了1个好方法。

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

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

nasm -f obj test.asm
val test.obj

游戏只是使用键盘箭头在固定屏幕上移动球拍,您也可以通过按下退出退出游戏.

这是一切都还好:https://puu.sh/yeKtG/affc912d4b.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], green
    add 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, [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

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, mydata
    mov ds, ax

    ;guardando el modo de video y aplicando el nuevo
    xor ax, ax
    mov ah, 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], ax
    mov ax, 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, black
    call DrawPaddle
    cmp word [paddleposition], 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, ax
    mov ah, 25h
    mov al, 9h
    int 21h
    mov al, 0
    mov ah, 4ch
    int 21h

谢谢阅读!



1> Ped7g..:

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

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


这里有一些建议如下:

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

我无法从头部记得什么是读取键盘的0x6X端口正确方法(我回想起刚才这一个有点棘手,把它完全正确的),所以我不会去检查特定的in/out序列及其正确性.

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

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

你对所有主循环使用慢速loop指令有多广泛,但是在delay子程序中你做了更快的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, 10000000b
        out     61h, al         ; set "enable kbd" bit
        mov     al, ah
        out     61h, al         ; set original value back

        mov     al, 20h
        out     20h, 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
        ...


推荐阅读
  • 深入解析 C 语言与 C++ 之间的差异及关联
    深入解析 C 语言与 C++ 之间的差异及关联 ... [详细]
  • 在稀疏直接法视觉里程计中,通过优化特征点并采用基于光度误差最小化的灰度图像线性插值技术,提高了定位精度。该方法通过对空间点的非齐次和齐次表示进行处理,利用RGB-D传感器获取的3D坐标信息,在两帧图像之间实现精确匹配,有效减少了光度误差,提升了系统的鲁棒性和稳定性。 ... [详细]
  • 本文将详细介绍在Android应用中添加自定义返回按钮的方法,帮助开发者更好地理解和实现这一功能。通过具体的代码示例和步骤说明,本文旨在为初学者提供清晰的指导,确保他们在开发过程中能够顺利集成返回按钮,提升用户体验。 ... [详细]
  • 如何在 Java LinkedHashMap 中高效地提取首个或末尾的键值对? ... [详细]
  • 本文详细介绍了如何在Linux系统中搭建51单片机的开发与编程环境,重点讲解了使用Makefile进行项目管理的方法。首先,文章指导读者安装SDCC(Small Device C Compiler),这是一个专为小型设备设计的C语言编译器,适合用于51单片机的开发。随后,通过具体的实例演示了如何配置Makefile文件,以实现代码的自动化编译与链接过程,从而提高开发效率。此外,还提供了常见问题的解决方案及优化建议,帮助开发者快速上手并解决实际开发中可能遇到的技术难题。 ... [详细]
  • Java 9 中 SafeVarargs 注释的使用与示例解析 ... [详细]
  • 深入解析Java中HashCode的功能与应用
    本文深入探讨了Java中HashCode的功能与应用。在Java中,HashCode主要用于提高哈希表(如HashMap、HashSet)的性能,通过快速定位对象存储位置,减少碰撞概率。文章详细解析了HashCode的生成机制及其在集合框架中的作用,帮助开发者更好地理解和优化代码。此外,还介绍了如何自定义HashCode方法以满足特定需求,并讨论了常见的实现误区和最佳实践。 ... [详细]
  • 本文深入探讨了 Vue.js 中异步组件的应用与优化策略。首先,文章介绍了异步组件的基本概念及其在现代前端开发中的重要性。为了确保最佳实践,建议使用 Webpack 作为模块打包工具,因为 Browserify 默认不支持异步组件的加载。接着,详细解释了异步组件的使用方法,并提供了官方文档的相关链接以供参考。此外,文章还讨论了多种优化技巧,包括代码分割、懒加载和性能调优,以提升应用的整体性能和用户体验。 ... [详细]
  • 本题库精选了Java核心知识点的练习题,旨在帮助学习者巩固和检验对Java理论基础的掌握。其中,选择题部分涵盖了访问控制权限等关键概念,例如,Java语言中仅允许子类或同一包内的类访问的访问权限为protected。此外,题库还包括其他重要知识点,如异常处理、多线程、集合框架等,全面覆盖Java编程的核心内容。 ... [详细]
  • C++ 进阶:类的内存布局与虚函数类的实现细节
    C++ 进阶:类的内存布局与虚函数类的实现细节 ... [详细]
  • Ceph API微服务实现RBD块设备的高效创建与安全删除
    本文旨在实现Ceph块存储中RBD块设备的高效创建与安全删除功能。开发环境为CentOS 7,使用 IntelliJ IDEA 进行开发。首先介绍了 librbd 的基本概念及其在 Ceph 中的作用,随后详细描述了项目 Gradle 配置的优化过程,确保了开发环境的稳定性和兼容性。通过这一系列步骤,我们成功实现了 RBD 块设备的快速创建与安全删除,提升了系统的整体性能和可靠性。 ... [详细]
  • BZOJ1034 详细解析与算法优化
    本文深入解析了BZOJ1034问题,并提出了优化算法。通过借鉴广义田忌赛马的贪心策略,当己方当前最弱的马优于对方最弱的马时进行匹配;同样地,若己方当前最强的马优于对方最强的马,也进行匹配。此方法在保证胜率的同时,有效提升了算法效率。 ... [详细]
  • 本文详细探讨了C语言中`extern`关键字的简易编译方法,并深入解析了预编译、`static`和`extern`的综合应用。通过具体的代码示例,介绍了如何在不同的文件之间共享变量和函数声明,以及这些关键字在编译过程中的作用和影响。文章还讨论了预编译过程中宏定义的使用,为开发者提供了实用的编程技巧和最佳实践。 ... [详细]
  • 【前端开发】深入探讨 RequireJS 与性能优化策略
    随着前端技术的迅速发展,RequireJS虽然不再像以往那样吸引关注,但其在模块化加载方面的优势仍然值得深入探讨。本文将详细介绍RequireJS的基本概念及其作为模块加载工具的核心功能,并重点分析其性能优化策略,帮助开发者更好地理解和应用这一工具,提升前端项目的加载速度和整体性能。 ... [详细]
  • 本文介绍了一种简化版的在线购物车系统,重点探讨了用户登录和购物流程的设计与实现。该系统通过优化界面交互和后端逻辑,提升了用户体验和操作便捷性。具体实现了用户注册、登录验证、商品浏览、加入购物车以及订单提交等功能,旨在为用户提供高效、流畅的购物体验。 ... [详细]
author-avatar
H小气淘淘_238
这个家伙很懒,什么也没留下!
PHP1.CN | 中国最专业的PHP中文社区 | DevBox开发工具箱 | json解析格式化 |PHP资讯 | PHP教程 | 数据库技术 | 服务器技术 | 前端开发技术 | PHP框架 | 开发工具 | 在线工具
Copyright © 1998 - 2020 PHP1.CN. All Rights Reserved | 京公网安备 11010802041100号 | 京ICP备19059560号-4 | PHP1.CN 第一PHP社区 版权所有