1

我这里有一个代码片段,它使用视频模式打印欢迎消息(菜单)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
4

2 回答 2

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
于 2013-05-02T05:46:23.390 回答
0

...在一个单独的 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. DisplayFile proc中仍然存在的CSinto复制,不能再工作了!DS
  • .STACK 1024此外,程序现在使用了更大的堆栈

建议

  • .STARTUP代码中,您设置了 80x25 16 色文本视频模式(两次!),但在DisplayFile proc 中有一系列指令,这些指令通常用于清除 320x200 256 色图形屏幕。这充其量是令人困惑的。但是因为你说这是游戏,所以视频模式13h是合理的。尽管如此,我建议将此清算委托给单独的 proc。
  • 此外,我认为没有理由在Displayfile proc中计算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

(*) Dirk Wolfgang Glomp 的回答显示了这样做的安全方式。

于 2021-11-22T19:59:23.673 回答