从文件中读取并在Assembly中输出其内容

问题描述 投票:0回答:2

我这里有一个代码片段,它使用视频模式打印欢迎消息(菜单)

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
assembly file-io x86 x86-16 tasm
2个回答
1
投票

如果我们启动我们的应用程序,那么我们就变成了 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

0
投票

...在一个单独的 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是合理的。尽管如此,我建议将此清算委托给单独的程序。
  • 此外,我认为没有理由在 Displayfile 过程中计算 BufferSeg 变量。它的值基于堆栈的位置,并且在程序执行期间几乎肯定会保持不变。
  • 将文件长度存储到内存变量不仅是一种浪费的操作,而且还使
  • 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 的

答案展示了执行此操作的安全方法。

© www.soinside.com 2019 - 2024. All rights reserved.