【问题标题】:Reading from a file and outputting its contents in Assembly从文件中读取并在 Assembly 中输出其内容
【发布时间】:2013-03-23 19:37:28
【问题描述】:

我这里有一个代码 sn-p,它使用视频模式 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

【问题讨论】:

  • 您确定BufferSeg 有效吗?您是否尝试过使用 int 21h / ah=48h 来分配内存?
  • 我尝试将DisplayScore proc near 分离到一个单独的文件中,它可以工作,即它从文件中读取并在屏幕上显示内容。但是当我将它并入视频模式时,就会出现垃圾值。请参阅编辑以查看单独的 asm 文件,尽管它与 DisplayScore 相同。
  • 原始代码还能编译吗?你有一个je @scores,我认为这是跳到下一行。但是那里的标签是@score(没有s)。
  • 哦,是的,我忘了在这里编辑它。但是,是的,原始代码可以编译。 :) 我不只是包括其他部分,因为有 1000 多行。

标签: assembly file-io x86 x86-16 tasm


【解决方案1】:

如果我们启动我们的应用程序,那么我们就变成了 DOS 的所有空闲内存,所以我们不知道我们实际可以使用多少内存,以及为 DOS 和 TSR 驱动程序保存了哪些段地址。因此,我们必须将目前还不需要的所有 ram 还给 DOS,然后才能从 DOS 请求新的指定 ram .

          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

【讨论】:

    【解决方案2】:

    ...在一个单独的 asm 文件中(只是为了测试从文件中读取是否有效)。

    DisplayFile proc 在从单独的程序中运行时可以正常工作,原因有以下两个:

    • 所有数据项都放在 de .CODE 部分,然后设置 DS 段寄存器就可以了,接下来的代码:

      mov     ax,cs
      mov     ds,ax
      
    • BufferSeg 的计算是正确的,因为它匹配.STACK 200h 设置:

      mov     bx,ss
      add     bx,200h/10h     ;get past the end of the file
      mov     [BufferSeg],bx
      

    为什么它在更大的程序中失败了?

    错误

    • 程序使用一个单独的.DATA 部分,您的.STARTUP 代码将DS 初始化为mov ax, @data mov ds, ax。将CS 复制到仍存在于DisplayFile proc 中的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
    ClarScreen 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
    

    (*)answer by Dirk Wolfgang Glomp 显示了执行此操作的安全方式。

    【讨论】:

      猜你喜欢
      • 2017-09-14
      • 1970-01-01
      • 1970-01-01
      • 2018-05-14
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      相关资源
      最近更新 更多