我这里有一个代码片段,它使用视频模式打印欢迎消息(菜单)
10h
。当按下 4 时,它应该从文件中读取并将其内容显示在屏幕上。但是,它显示垃圾值,我必须打开 DOSBox 并再次挂载。
.model small
.stack 1024
.data
MENU DB 10,""
DB 10," Welcome " ;24
DB 10,""
DB 10,"1 Novice" ;3, 9
DB 10,"2 Boss" ;3, 6
DB 10,"3 Superb" ;3, 11
DB 10,"4 Scores"
DB 10,""
DB 10,"Choice: ","$" ;8
ROW1 DB 5
ROW2 DB 10
COL DB 25
Choice DB ?
; OTHER DECLARATIONS FOR COLORING I WON'T SHOW FOR SIMPLICITY
FileName DB "file.txt",0,8 ; name of file to open
Handle DW ? ; to store file handle
BufferSeg dw 0
ErrMsgOpen db "Error opening `"
FileLength dw 0
nextLine db 13,10
.code
DisplayFile PROC NEAR
;escape to video mode
mov ax,0A000h
mov es,ax
xor di,di
xor ax,ax
mov cx,32000d
cld
rep stosw
mov ax,cs
mov ds,ax
mov bx,ss
add bx,200h/10h ;get past the end of the file
mov [BufferSeg],bx ;store the buffer segment
;call WriteFile
push ds
mov ax,cs
mov ds,ax
mov ax,3d00h ;open file (ah=3dh)
mov dx,offset FileName
int 21h
mov bx,ax ;move the file handle into bx
mov ds,[BufferSeg]
mov dx,0 ;load to [BufferSeg]:0000
mov ah,3fh
mov cx,0FFFFh ;try to read an entire segments worth
int 21h
mov [cs:FileLength],ax
mov ah,3eh
int 21h ;close the file
cld
mov si,0
mov cx,[cs:FileLength]
PrintLoop:
mov ah,2
lodsb
mov dl,al
int 21h ;print a character
dec cx
jne PrintLoop
pop ds
ret
OpenError:
mov ah,9
mov dx,offset ErrMsgOpen
int 21h
pop ds
ret
DisplayFile ENDP
.STARTUP
mov ax, @data
mov ds, ax
@welcome:
mov ax, 3
int 10h
MOV AX, 3 ; 80x25 color
INT 10H ; video BIOS call
MOV AH, 2 ; set cursor position
MOV BH, 0 ; display page number
MOV DH, ROW1 ; row number
MOV DL, COL ; column number
INT 10H ; video BIOS call
LEA BP, ATT_BRICK ; point to first attribute array
CALL FAR PTR STICK ; display first line of video text
;scanf user's choice
mov ah, 01h
int 21h
sub al, '0'
mov Choice, al
; OTHER CODES
cmp al, 4
je @scores
@score:
call DisplayFile
@quit:
mov ax, 4c00h ;call dos to exit
int 21h
.EXIT
END
基本上这是一个将结果保存在文件中的游戏。我可以正确写入文件,但是当我尝试读取文件时,它不会输出到屏幕上。
编辑
这与
DisplayScore proc near
的作用相同,但在单独的 .ASM 文件中。它只是为了测试从文件读取是否有效,并且确实有效。
.MODEL SMALL
.STACK 200h
.CODE
Ideal
;===- Data -===
BufferSeg dw 0
ErrMsgOpen db "Error opening `"
FileName db "file.txt",0,8,"'$" ;8 is a delete character
;0 is required for filename
;(displays a space)
FileLength dw 0
buffer db "hehe$"
;===- Subroutines -===
PROC DisplayFile NEAR
push ds
mov ax,cs
mov ds,ax
mov ax,3d00h ;open file (ah=3dh)
mov dx,offset FileName
int 21h
jc OpenError
mov bx,ax ;move the file handle into bx
mov ds,[BufferSeg]
mov dx,0 ;load to [BufferSeg]:0000
mov ah,3fh
mov cx,0FFFFh ;try to read an entire segments worth
int 21h
mov [cs:FileLength],ax
mov ah,3eh
int 21h ;close the file
cld
mov si,0
mov cx,[cs:FileLength]
PrintLoop:
mov ah,2
lodsb
mov dl,al
int 21h ;print a character
dec cx
jne PrintLoop
pop ds
ret
OpenError:
mov ah,9
mov dx,offset ErrMsgOpen
int 21h
pop ds
ret
ENDP DisplayFile
;===- Main Program -===
START:
mov ax,cs
mov ds,ax
mov bx,ss
add bx,200h/10h ;get past the end of the file
mov [BufferSeg],bx ;store the buffer segment
;call WriteFile
call DisplayFile
mov ax,4c00h
int 21h
END START
如果我们启动我们的应用程序,那么我们就变成了 DOS 中的所有空闲 RAM,因此我们无法知道我们实际可以使用多少 RAM 以及为 DOS 和 TSR 驱动程序保存了哪些段地址。因此,我们必须将目前还不需要的所有 RAM 归还给 DOS,然后才能从 DOS 请求新指定的 RAM 数量,从一个连续具有空闲 RAM 的段地址开始,保证不从 DOS 或驱动程序中使用.
call SETFREE ; calculate the amoung of ram that we need
; for running our application and giviving
; back the rest of ram to DOS
mov bx, 2000h ; request/reserv 128 KB ram from DOS
call GETSPACE
jc NOSPACE ; Error!
mov [NEWSEG], ax ; save segment address
;------------------------------------
SETFREE: mov bx, ss ; First we subtract both segmentaddresses
mov ax, es ; for to become the amoung of paragraphs
sub bx, ax ; from the PSP to the beginning of the stack.
mov ax, sp ; Because our stackpointer beginn at the end
add ax, 0Fh ; of the stacksegment, we can use the stackpointer
shr ax, 4 ; for the length of the stack.
add bx, ax
mov ah, 4Ah ; Set new size
int 21h
ret
;------------------------------------
GETSPACE: mov ah, 48h ; BX = number/16
int 21h
ret
...在一个单独的 asm 文件中(只是为了测试从文件读取是否有效)。
由于以下两个原因,DisplayFile 过程在从单独的程序操作时工作正常:
所有数据项都放置在 de
.CODE
部分,并且设置 DS
段寄存器可以通过以下代码完成:
mov ax, cs
mov ds, ax
BufferSeg的计算是正确的,因为它与
.STACK 200h
设置匹配:
mov bx, ss
add bx, 200h/10h ; Get to past the end of the file
mov [BufferSeg], bx
.DATA
部分,您的 .STARTUP
代码将使用 DS
mov ax, @data
初始化 mov ds, ax
。将 CS
复制到仍存在于 DisplayFile过程中的
DS
中,无法再工作!.STACK 1024
),但您仍在计算 BufferSeg,就好像堆栈大小为 512 一样。这意味着加载文件将覆盖堆栈并可能导致程序崩溃!.STARTUP
代码中,您设置了 80x25 16 色文本视频模式(两次!),但在 DisplayFile 过程中,有一系列用于清除 320x200 256 色图形屏幕的典型指令。这充其量是令人困惑的。但因为你说这是一个游戏,所以视频模式13h是合理的。尽管如此,我建议将此清算委托给单独的程序。DS
段寄存器的使用变得复杂。
.DATA
部分 (
Buffer db 512 dup (0)
) 中定义的简单通用缓冲区。我们不再需要处理改变
DS
。
ClearScreen PROC NEAR
push es
mov ax, 0A000h
mov es, ax
xor di, di
mov cx, 32000
xor ax, ax
rep stosw
pop es
ret
ClearScreen ENDP
DisplayFile PROC NEAR
push ds
call ClearScreen
mov ax, 3D00h ; DOS.OpenFile for reading
mov dx, OFFSET FileName
int 21h ; -> AX CF
jc FileError
mov bx, ax ; Handle
mov ds, [BufferSeg]
xor dx, dx ; Load to [BufferSeg]:0000
mov cx, 512 ; Try to read a 'reasonable' number of bytes
mov ah, 3Fh ; DOS.ReadFile
int 21h ; -> AX CF
jc FileError
mov cx, ax ; Bytes read
mov ah, 3Eh ; DOS.CloseFile
int 21h ; -> AX CF
jc FileError
jcxz FileWasEmpty
xor si, si
PrintLoop:
lodsb
mov dl, al
mov ah, 02h ; DOS.PrintCharacter
int 21h
dec cx
jnz PrintLoop
FileWasEmpty
pop ds
ret
FileError:
pop ds ; Restore DS first!
mov dx, OFFSET ErrMsgOpen
mov ah, 09h ; DOS.PrintString
int 21h
ret
DisplayFile ENDP
.STARTUP
cld
mov ax, @data
mov ds, ax
mov ax, ss
add ax, 1024/16 ; Available memory (*)
mov [BufferSeg], ax
...
call DisplayFile
...
mov ax, 4C00h ; DOS.Terminate
int 21h
(*) Dirk Wolfgang Glomp 的