ASM x86 如何从数组中正确获取指针? (16 位 TASM / DOS)

ASM x86 How to get the pointer from an array properly? (16-bit TASM / DOS)

好吧伙计们,让我们希望这是一个简单的问题:我需要访问一个数组以在 16 位 TASM 中实现双缓冲(我使用模式 13h)。但是:无论我使用 "OFFSET"、"BYTE PTR [Array]"、"BYTE PTR Array" 还是我已经尝试过的任何方法,程序 reads/writes 都会指向不正确的内存块,这部分位于数组的实际开始。

这是我的代码(目前还没有真正优化而且非常混乱):

.MODEL  MEDIUM
.STACK
.DATA
        XPos    DW      0
        YPos    DB      0
        Color   DB      0

        BoxX1   DW      0
        BoxY1   DB      0
        BoxX2   DW      0
        BoxY2   DB      0

        VPage   DB      64010   DUP(0)  ;TODO: Size *might* be incorrect.
        PageSeg DW      0
.CODE

SetVGA13 PROC
        MOV     AX,     0013h   ;Screen mode 13.
        INT     10h             ;Set screen mode to AX.
        MOV     AX,     0A000h  ;Screen segment.
        MOV     ES,     AX      ;You can't affect segment registers
        RET
ENDP

;-------DrawPixel---------------
; WORD XPos  = x
; WORD YPos  = y
; BYTE Color = colour
;-------------------------------
DrawPixel PROC
        XOR     AH,     AH
        MOV     AL,     [YPos]
        MOV     DX,     320
        MUL     DX
        ADD     AX,     [XPos]
        MOV     DI,     AX
        MOV     AL,     [Color]
        MOV     ES,     [PageSeg]  
        ;ADD     ES,     DI
        MOV     ES:[DI],AL
        ;MOV     ES:[DI],AL
        RET
ENDP

DrawBox PROC
        MOV CL, [BoxY1]
        YLoop:
        MOV     BL,     CL
        PUSH    CX
        MOV     CX,     [BoxX1]
        XLoop:
        MOV     [XPos], CX
        MOV     [YPos], BL
        MOV     [Color],CL
        CALL    DrawPixel
        INC     CX
        CMP     CX,     [BoxX2]
        JNZ     XLoop
        POP     CX
        INC     CL
        CMP     CL,     [BoxY2]
        JNZ     YLoop
        RET
ENDP

WaitFrame PROC
        PUSH    DX
        ; Port #03DA contains VGA status
        MOV     DX,     03DAh
        IN      AL,     DX
        WaitRetrace:
        ; Bit 3 will be on if we're in retrace
        TEST    AL,     08h
        JNZ     WaitRetrace
        EndRefresh:
        IN      AL,     DX
        TEST    AL,     08h
        JZ      EndRefresh
        POP     DX
        RET
ENDP

RestoreVideo PROC
        ; Return to text mode
        MOV     AX,     03h
        INT     10h
        RET
ENDP

ClearScreen PROC
        XOR     CX,     CX
        ;MOV     ES,     [PageSeg] 

        ClearLoop:
        MOV     DI,     CX  
        ;MOV     ES,     [PageSeg]
        MOV     BX,     OFFSET VPage
        ADD     BX,     CX
        MOV     AL,     [BX];VPage[DI];ES:[DI] 
        MOV     [Color],AL 

        MOV     AX,     0A000h
        MOV     ES,     AX
        MOV     AL,     [Color]
        MOV     ES:[DI],AL

        INC     CX
        CMP     CX,     64000
        JNZ     ClearLoop
        RET
ENDP

Main:
        ;INITIALISE
        MOV     BX,     OFFSET VPage
        MOV     [PageSeg],BX


        CALL    SetVGA13
        ;CALL    MakePalette       

        MOV     [BoxX1],33
        MOV     [BoxY1],33
        MOV     [BoxX2],99
        MOV     [BoxY2],99

        ;LOOP
        GameLoop:
        ;DRAW
        ;CALL    DrawBox
        CALL    ClearScreen
        ;CALL    WaitFrame

        ;INPUT
        MOV     DX,     60h
        IN      AL,     DX

        CMP     AL,     75
        JNZ     NotLeft
        SUB     [BoxX1],1
        SUB     [BoxX2],1
        NotLeft:

        IN      AL, DX
        CMP     AL, 77
        JNZ     NotRight
        ADD     [BoxX1],1
        ADD     [BoxX2],1
        NotRight:


        CMP     AL,     1
        JNZ     GameLoop



        ;END PROGRAM
        Error:

        ;CALL    ClearScreen
        CALL    RestoreVideo
        MOV     AH,     4Ch
        INT     21h
        END     Main

此代码显示了一个彩虹色的框,您可以使用左右箭头键四处移动,

;INITIALISE
        MOV     BX,     OFFSET VPage
        MOV     [PageSeg],BX

这是我试图获取指向我的缓冲区的指针的不幸尝试,但 return 不是正确的

抱歉,我的问题没有完成,我意识到当我出于某种原因立即起床时。

虽然这不是我编写代码的方式,但我会提供一些建议,可能会让您更接近于解决问题。

当您的可执行文件加载 DSES 寄存器时,最初指向您程序的 DOS PSP。在您的情况下,您至少需要将 DS 指向您的 DATA 段。由 DOS EXE 加载程序在 运行 时间填充的数据段可以通过在段名称前加上 @ (AT) 符号在您的代码中引用。所以你可以替换这段代码:

;INITIALISE
MOV     BX,     OFFSET VPage
MOV     [PageSeg],BX

有了这个:

;INITIALISE
MOV BX, @DATA           ; Set up DS with our program's DATA segment
MOV DS, BX              
MOV     [PageSeg],BX    ; VPage is in DATA segment, move segment to PageSeg

在您的 DrawPixel 代码中,您需要将 VPage 偏移添加到 DI。替换此代码:

XOR     AH,     AH
MOV     AL,     [YPos]
MOV     DX,     320
MUL     DX
ADD     AX,     [XPos]
MOV     DI,     AX
MOV     AL,     [Color]
MOV     ES,     [PageSeg]  
MOV     ES:[DI],AL

与:

XOR     AH,     AH
MOV     AL,     [YPos]
MOV     DX,     320
MUL     DX
ADD     AX,     [XPos]
MOV     DI,     AX
MOV     AL,     [Color]
MOV     ES,     [PageSeg]  
ADD     DI,     OFFSET VPage    ; We need to add VPage's offset 
                                ; from beginning of PageSeg
MOV     ES:[DI],AL

视频模式 13h 为 320x200x256 色。所需的视频 ram 数量为 320*200*1 = 64000。这可以更改为:

VPage   DB      64010   DUP(0)  ;TODO: Size *might* be incorrect. 

至:

VPage   DB      64000   DUP(0)

通过这些更改,程序似乎显示了一个彩虹框,并使用箭头键在屏幕上左右移动。

可能还有其他错误,但我不知道您要达到什么目的。这段代码可以大大简化。我的更改是最小的更改,以配合您编写程序的方式并使其具有一定的功能性。