我编写的游戏类似于汇编中的突破(工具:DosBOX,notepad ++,tasm),在图形模式下创建边框和平台时,我偶然发现了两个问题:
这是我目前的代码,平台创建边框,平台和移动所涉及的主要步骤是BorderPaint
,PlatBuild
和PlatMove
IDEAL
MODEL small
STACK 100h
DATASEG
platLoc dw 63806
speed1 dw 10
speed2 dw 0FFFFh
CODESEG
;procedure to paint borders-6 px white pixels
proc BorderPaint
push ax
push bx
push di
push cx
mov ax,0A000h ;accesses graphics mode video memory
mov es,ax
xor di,di
;first line-left vertical
;sets color white
mov ax,0Fh
mov cx,200 ;enters nested loop
FirstLoop1:
push cx
mov cx,6
SecondLoop1:
mov [es:di],ax
inc di
loop SecondLoop1
sub di,6
add di,320
pop cx
loop FirstLoop1
;second line- up horizontal
mov di,7
mov cx,313
FirstLoop2:
push cx
mov cx,6
SecondLoop2:
mov [es:di],ax
add di,320
loop SecondLoop2
sub di,1920
inc di
pop cx
loop FirstLoop2
mov di,314
mov cx,200
FirstLoop3:
push cx
mov cx,6
SecondLoop3:
mov [es:di],ax
inc di
loop SecondLoop3
sub di,6
add di,320
pop cx
loop FirstLoop3
; xor di,di
; mov ax,0Fh
; mov cx,200
; FirstLoop4:
; push cx
; mov cx,6
; SecondLoop4:
; mov [es:di],ax
; inc di
; loop SecondLoop4
; sub di,6
; add di,320
; pop cx
; loop FirstLoop4
pop cx
pop di
pop bx
pop ax
ret
endp BorderPaint
proc PlatBuild
push di
push cx
push ax
mov di,[platLoc]
mov ax,5
mov cx,40
LoopPaint2:
push cx
mov cx,10
LoopPaint1:
mov [es:di],ax
sub di,320
loop LoopPaint1
add di,320*10
inc di
pop cx
loop LoopPaint2
pop ax
pop cx
pop di
ret
endp PlatBuild
Proc PlatErase
push di
push cx
push ax
mov di,[platLoc]
mov ax,0
mov cx,40
LoopPaint4:
push cx
mov cx,10
LoopPaint3:
mov [es:di],ax
sub di,320
loop LoopPaint3
add di,320*10
inc di
pop cx
loop LoopPaint4
pop ax
pop cx
pop di
ret
endp PlatErase
Proc PlatMove
push ax
push cx
push dx
push di
mov ax,0A000h
mov es,ax
call BorderPaint
;Input
CheckPress1:
mov ah,0h
int 16h
cmp al,'a'
jne nxt11
jmp MoveLeft1
Nxt11:
cmp al,'d'
jne nxt12
jmp MoveRight1
Nxt12:
cmp al,'q'
jne CheckPress1
jmp endproc1
MoveLeft1:
call Delay
mov dx,[platLoc]
add dx,8*320
sub dx,1d
mov di,dx
mov ax,[es:di]
cmp ax,0Fh
jne move1
jmp CheckPress1
move1:
call PlatErase
dec [platLoc]
call PlatBuild
mov ah,1h
int 16h
je MoveLeft1
jmp CheckPress1
MoveRight1:
call Delay
mov dx,[platLoc]
add dx,41
mov di,dx
mov ax,[es:di]
cmp ax,0Fh
jne move2
jmp CheckPress1
move2:
call PlatErase
inc [platLoc]
call PlatBuild
mov ah,1h
int 16h
je MoveRight1
jmp CheckPress1
EndProc1:
pop di
pop dx
pop cx
pop ax
ret
endp PlatMove
proc Delay
push cx
mov cx, [speed2]
LoopLong:
push cx
mov cx, [speed1]
LoopShort:
loop LoopShort
pop cx
loop LoopLong
pop cx
ret
Endp Delay
start:
mov ax,@data
mov ds,ax
mov ax,13h
int 10h
call BorderPaint
call PlatBuild
mov ax, [es:63680]
call PlatMove
exit:
mov ax,4c00h
int 21h
END startenter code here
答案 0 :(得分:1)
mov [es:di],ax
将WORD(两个字节)写入VRAM ax = 0x000F
,因此您以[es:di]
值0x0F
和[es:di+1]
值{{1}写入(黑色)。0x00
这是亵渎:/ ... sub di,6
add di,320
仍然是源级别上可见的意图,但也显示出对机器的无知,减少了使用的指令量。
顺便说一句,如果你想存储6个0x0F值的字节,你还可以存储三个字的0x0F0F值,节省一半的VRAM访问(这是很昂贵的)。那么
add di,320-6
是更优化的硬编码方式,如何绘制6个像素(只有3个VRAM写入)并在下面移动1行。
mov di,<some VRAM address>
mov cx,<number of lines>
mov ax,0F0Fh ; <color><color> (two pixels at same time)
Draw6PixelsLoop:
mov [es:di],ax
mov [es:di+2],ax
mov [es:di+4],ax
add di,320
dec cx
jnz Draw6PixelsLoop
中从VRAM读取单词(两个像素),然后将它们与0Fh PlatMove
进行比较,即两个像素“白色+黑色” = cmp ax,0Fh
。因此,在左侧,这可能会很好,但在右侧,它将只在边框的末端找到这样的两个像素(请记住,在右侧的最后一个像素之后,下一个字节是第一个左侧像素下一行,所以如果你有6像素强的边框,第一个白色+黑色像素位于左边框结束的下一行)。答案 1 :(得分:1)
由于您的程序使用320x200 256色视频模式13h,因此每个像素都由VRAM中的单个字节表示。代码中最重要的问题,也就是Ped7g已经指出这一点,就是你一直在读写单词,你应该在那里阅读和写字节。
我将通过编写上边界的修正来表明我们的意思。 不要忘记在其他地方进行类似的更改!
;second line- up horizontal
mov di,6 ; (6,0) <--- See 1.
mov cx,320-6-6 ; Width <--- See 2.
FirstLoop2:
push cx
mov cx,6 ; Height
SecondLoop2:
mov [es:di],al ; Color 0Fh, Byte from AL instead of word from AX
add di,320 ; Go down 1 line
loop SecondLoop2
sub di,320*6 ; Take back 6 additions of 320
inc di ; Go right 1 pixel
pop cx
loop FirstLoop2
我选择此部分是因为它还有一些其他错误:
上面的代码可以进行很多优化,但目前你正努力让事情发挥作用。因此,请不要尝试一次处理超过1个像素。编程的一个重要座右铭是:“首先让它发挥作用,然后让它变得更好”。
也写下我写的评论。它有助于理解你在做什么。
MoveLeft1: call Delay mov dx,[platLoc] add dx,8*320 ??? sub dx,1d mov di,dx mov ax,[es:di] ??? cmp ax,0Fh ???
在 MoveLeft1 代码中,您从错误计算的地址中读取!通过在dx
注册表中添加8 * 320,您可以在屏幕下方 ,所以屏幕外。实际上由于分段环绕,你将错误地处理VRAM顶部的一些随机像素。
MoveLeft1:
call Delay
mov di, [platLoc]
mov al, [es:di-1] ; Pixel to the left of the platform
cmp al, 0Fh ; Is it white?
jne move1
jmp CheckPress1
move1:
call PlatErase
dec [platLoc]
call PlatBuild
mov ah, 01h
int 16h ; Key pending?
je MoveLeft1
jmp CheckPress1
MoveRight1: call Delay mov dx,[platLoc] add dx,41 ??? mov di,dx mov ax,[es:di] ??? cmp ax,0Fh ???
在 MoveRight1 代码中,您还会从错误的地址读取。平台宽度为40像素,因此最后一个像素位于 platLoc + 39,因此需要检查的像素位于 platLoc + 40。
MoveRight1:
call Delay
mov di, [platLoc]
mov al, [es:di+40] ; Pixel to the right of the platform
cmp al, 0Fh ; Is it white?
jne move2
jmp CheckPress1
move2:
call PlatErase
inc [platLoc]
call PlatBuild
mov ah, 01h
int 16h ; Key pending?
je MoveRight1
jmp CheckPress1