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 不是正确的
抱歉,我的问题没有完成,我意识到当我出于某种原因立即起床时。
虽然这不是我编写代码的方式,但我会提供一些建议,可能会让您更接近于解决问题。
当您的可执行文件加载 DS 和 ES 寄存器时,最初指向您程序的 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)
通过这些更改,程序似乎显示了一个彩虹框,并使用箭头键在屏幕上左右移动。
可能还有其他错误,但我不知道您要达到什么目的。这段代码可以大大简化。我的更改是最小的更改,以配合您编写程序的方式并使其具有一定的功能性。
好吧伙计们,让我们希望这是一个简单的问题:我需要访问一个数组以在 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 不是正确的
抱歉,我的问题没有完成,我意识到当我出于某种原因立即起床时。
虽然这不是我编写代码的方式,但我会提供一些建议,可能会让您更接近于解决问题。
当您的可执行文件加载 DS 和 ES 寄存器时,最初指向您程序的 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)
通过这些更改,程序似乎显示了一个彩虹框,并使用箭头键在屏幕上左右移动。
可能还有其他错误,但我不知道您要达到什么目的。这段代码可以大大简化。我的更改是最小的更改,以配合您编写程序的方式并使其具有一定的功能性。