在不更改默认段定义大小的情况下在 MASM 中使用 32 位寄存器
Using 32 bit registers in MASM without changing the default segment definition size
我的代码对于 8086 和 80286 处理器来说太慢了,所以我决定在我的实模式代码中使用 32 位寄存器和指令。
我知道我真正需要做的就是在单个指令前加上 66h,但如果您不在程序集的最顶部包含 .386 指令,MASM 将不接受 386 个寄存器文件。
这样做之后,我发现我的程序不再运行,即使我没有使用任何 386 寄存器。它在黑屏时挂起,然后 DOSBox 崩溃。这种行为通常表示我的程序中出现堆栈崩溃和内存损坏。
在 MASM 5.10(我使用的版本)的文档中,我找到了以下信息:“如果在 .MODEL 指令之前使用 .386 指令,段
definitions 定义 32 位段。如果要启用具有 16 位段的 80386 处理器,应该在 .MODEL 指令之后给出 .386 指令。"
我很确定这是我的问题,我需要包含一个 .MODEL 指令
将确保段保持 16 位。在使用 .386 指令之前,我尝试在我的主程序集文件中包含所有列出的 .MODEL 指令(文档将它们称为最常见的模型)。它们都会产生错误,我认为这可能是由于我没有在构成我的程序的其他十几个汇编文件中包含 .MODEL 指令这一事实造成的。我只想继续使用默认的 .MODEL,无论它是什么。
直到现在我都不需要使用 .MODEL 指令,文档没有提到默认使用哪个,或者在使用 .386 时保持 16 位段不变。
.MODEL SMALL、.MODEL MEDIUM 和 .MODEL COMPACT 都会产生许多 linker 错误,如下所示:错误 L2002:段代码位置 0016 处的修复溢出:1FA 记录 type:48A8
.MODEL LARGE 和 .MODEL HUGE assemble 和 link 很好,但几帧后我的程序崩溃,一些垃圾转储到视频内存中,可能是堆栈崩溃。同样,我目前没有在其他十几个汇编文件中包含 .MODEL 指令。
我想要的是偶尔能够使用 386 寄存器和指令,但除此之外我希望程序的行为与以往完全相同。处理所有段,例如 16 位。
这是我的主要装配文件,我不太确定这是哪个型号。大,也许?没有单个段大于 64k,所以可能不会。有一个堆栈段和一个代码段,但有几个数据段。所有这些都是 public 并在构成程序的整个程序集文件中共享。
theStack SEGMENT STACK
db 64 dup ('THESTACK') ;512 byte stack
theStack ENDS
varData SEGMENT PUBLIC
INCLUDE const.inc ;global constants
PUBLIC fCntr
fCntr db 0 ;A frame counter used to delay animations.
varData ENDS
frame SEGMENT PUBLIC
db scrArea dup (247d) ;64,000 byte frame buffer
frame ENDS
field SEGMENT PUBLIC
db 65535 dup ('F') ;64k buffer that holds up to 32,768 tile indexes
field ENDS
sprites SEGMENT PUBLIC
db 65535 dup ('S') ;64k buffer for animated spites
sprites ENDS
code SEGMENT PUBLIC
EXTRN SET_VGA_256:PROC,INIT_DISK_VARS:PROC,INIT_AREA:PROC,CALC_DELAY:PROC
EXTRN HANDLE_INPUT:PROC,UPDATE_SPRITES:PROC,DRAW_SPRITES:PROC
EXTRN DRAW_FIELD:PROC,WRITE_FRAME:PROC,FRAME_DELAY:PROC,EXIT2DOS:PROC
EXTRN DBG:PROC
assume cs:code,ds:varData
main PROC
start:
mov ax, varData
mov ds, ax ;Load the variable segment into ds
cld ;ensure that string operations auto-increment
call SET_VGA_256 ;Set the video mode to 320x200 256 colors.
call INIT_DISK_VARS ;Setup hard drive access variables
call INIT_AREA ;Build the area into memory from data files
call CALC_DELAY ;calculate the frame delay using the RTC
LOOP_TILL_ESC:
call HANDLE_INPUT ;Handle user input.
call UPDATE_SPRITES ;bounds check then move the sprites
call DRAW_FIELD ;draw the tiles that make up the play field
call DRAW_SPRITES ;draw all currently visible sprites
call WRITE_FRAME ;Write the frame buffer to video memory.
inc fCntr ;increment the frame counter
call FRAME_DELAY ;delay for the specified number of milliseconds
cmp bp, 1 ;Was the Esc key pressed?
jne LOOP_TILL_ESC ;If not, loop back through the main program.
call EXIT2DOS ;If so, return to DOS.
main ENDP
code ENDS
END start
这里有一个简单的程序,如果使用 .386 就会中断。它应该用粉红色像素填充屏幕,但它却挂在黑屏上并使 DOSBox 崩溃。
.MODEL SMALL
.386
theStack SEGMENT STACK
db 64 dup ('THESTACK')
theStack ENDS
code SEGMENT PUBLIC
assume cs:code,ds:varData
main PROC
start:
mov ax, varData
mov ds, ax ;Load the variable segment into ds
cld ;ensure that string ops auto-increment
xor ah, ah ;select set video mode function
mov al, 13h ;320x200 256 colors
int 10h ;video mode set
mov di, 0a000h
mov es, di
xor di, di ;es:di -> vga pixel buffer
mov ah, 64d
mov al, ah ;ah & al -> pink color index byte
mov cx, 32000d ;writing 32,000 words
rep stosw ;fill the screen with pink pixels
ESC_LOOP:
in al, 60h
cmp al, 1
jne ESC_LOOP ;delay till escape key is pressed
mov ax, 40h
mov es, ax ;access keyboard data area via segment 40h
mov WORD PTR es:[1ah], 1eh ;set the kbd buff head to start of buff
mov WORD PTR es:[1ch], 1eh ;set the kbd buff tail to same as buff head
;now the keyboard buffer is cleared.
xor ah, ah ;select video mode function
mov al, 3 ;select 80x25 16 colors
int 10h ;restore VIDEO back to text mode
mov ah, 4ch ;Terminate process DOS service
xor al, al ;Pass 0 to ERRORLEVEL
int 21h ;Control returns to DOS
main ENDP
code ENDS
END start
我错误地认为 .386 指令属于汇编文件的最顶部。实际上它属于代码段定义。在堆栈定义上方包含 .386 会导致堆栈的对齐类型为 DWORD 而不是 WORD,这意味着像 push ax
这样的堆栈操作被视为 push eax
。这破坏了与期望 16 位堆栈的代码的兼容性,这就是我的程序崩溃的原因。
在使用 .386 之后定义任何其他段之前需要使用 .8086 指令。
您对问题的描述并非 100% 正确,但您的 "pink" 样本来源是充分解释问题的好例子。
.MODEL
指令与.CODE, .CONST, .DATA, .DATA?, .FARDATA, .FARDATA?, .STACK
简化段指令。
所以在工作方式和 16b DOS 可执行文件中使用它们的一种方法是这样的:
.MODEL SMALL
.386
.STACK 100h
.DATA
x DB 1
.CODE
start:
mov ax,@DATA
mov ds,ax
movzx eax,BYTE PTR [x]
mov ah,4Ch
int 21h
END start
仅使用简化指令,.CODE
将定义名为 _TEXT
的代码段,这是 16b 实模式代码段(由于 .386
指令被放置 after .MODEL SMALL
指令).
您的 "pink" 示例不使用简化的段指令,而是使用完整的段指令,然后您必须在代码段定义中指定它用于实模式,就像在下一个固定源中一样,它将填满屏幕首先是粉红色(使用 16b 寄存器),然后是一些青色键(在实模式下使用 32b 寄存器)。
我必须将 USE16
添加到 code SEGMENT
指令中,以正确设置它,然后生成的 32b 指令以正确的方式为 16b 实模式添加前缀(即不同于32b 保护模式)。
我进一步测试了当你将显式代码段定义与简化的 .CODE
指令混合时会发生什么,令人惊讶的是(对我来说)最终的 .exe 有两个代码段,即使 .MODEL SMALL
模型... 所以"dotCode"段中的测试过程只能通过FAR调用才能到达。至少 .CODE
段被正确分配为 16b 段,因此生成的程序集按预期工作。
固定示例(使用 TASM 4.1 + TLINK 测试,仅 运行 带有文件名且没有选项,应生成 ASM -> OBJ -> EXE 文件):
.MODEL SMALL
.386
theStack SEGMENT USE16 STACK
db 64 dup ('THESTACK')
theStack ENDS
; test of simplified code segment directive
.CODE
testDotCode PROC
mov eax,12345678h
retf
testDotCode ENDP
ENDS
code SEGMENT USE16 PUBLIC
assume cs:code, ss:theStack
main PROC
call FAR PTR testDotCode ; test code inside simplified code segment definition
; with experiment I find out, that even with ".MODEL SMALL" the TASM+TLINK will
; put the testDotCode subroutine into new "_TEXT" code segment!
; So only FAR call + retf works to access it.
cld ; ensure that string ops auto-increment
mov ax, 13h ; select set video mode function: 13h 320x200 256 colors
int 10h ; video mode set
mov di, 0a000h
mov es, di ; es = VRAM segment
; original 16b test code - fill screen with pink
xor di, di ;es:di -> vga pixel buffer
mov ah, 64d
mov al, ah ;ah & al -> pink color index byte
mov cx, 32000d ;writing 32,000 words
rep stosw ;fill the screen with pink pixels
; wait for any key
xor ah,ah
int 16h
; 32b test code to validate ".386" setup success in real mode
xor di, di ; es:edi -> vga pixel buffer
mov eax, 34343434h ; eax = 4x cyan color
mov ecx, 320*200/4 ; full screen fill
rep stosd ; fill the screen with pink pixels
; wait for any key
xor ah,ah
int 16h
; restore text mode (3)
mov ax,3 ; select video mode: text 80x25 16 colors
int 10h
mov ax,4C00h ; terminate DOS process with 0 ERRORLEVEL
int 21h
main ENDP
code ENDS
END main
我主要使用此网页作为这些详细信息的来源:http://www.c-jump.com/CIS77/ASM/Directives/lecture.html#D77_0070_code_directive
并且此答案并未详尽替换正确的 TASM/MASM 文档,不幸的是,只是(希望完整)解释导致您出现问题的原因。
我的代码对于 8086 和 80286 处理器来说太慢了,所以我决定在我的实模式代码中使用 32 位寄存器和指令。
我知道我真正需要做的就是在单个指令前加上 66h,但如果您不在程序集的最顶部包含 .386 指令,MASM 将不接受 386 个寄存器文件。
这样做之后,我发现我的程序不再运行,即使我没有使用任何 386 寄存器。它在黑屏时挂起,然后 DOSBox 崩溃。这种行为通常表示我的程序中出现堆栈崩溃和内存损坏。
在 MASM 5.10(我使用的版本)的文档中,我找到了以下信息:“如果在 .MODEL 指令之前使用 .386 指令,段 definitions 定义 32 位段。如果要启用具有 16 位段的 80386 处理器,应该在 .MODEL 指令之后给出 .386 指令。"
我很确定这是我的问题,我需要包含一个 .MODEL 指令 将确保段保持 16 位。在使用 .386 指令之前,我尝试在我的主程序集文件中包含所有列出的 .MODEL 指令(文档将它们称为最常见的模型)。它们都会产生错误,我认为这可能是由于我没有在构成我的程序的其他十几个汇编文件中包含 .MODEL 指令这一事实造成的。我只想继续使用默认的 .MODEL,无论它是什么。
直到现在我都不需要使用 .MODEL 指令,文档没有提到默认使用哪个,或者在使用 .386 时保持 16 位段不变。
.MODEL SMALL、.MODEL MEDIUM 和 .MODEL COMPACT 都会产生许多 linker 错误,如下所示:错误 L2002:段代码位置 0016 处的修复溢出:1FA 记录 type:48A8
.MODEL LARGE 和 .MODEL HUGE assemble 和 link 很好,但几帧后我的程序崩溃,一些垃圾转储到视频内存中,可能是堆栈崩溃。同样,我目前没有在其他十几个汇编文件中包含 .MODEL 指令。
我想要的是偶尔能够使用 386 寄存器和指令,但除此之外我希望程序的行为与以往完全相同。处理所有段,例如 16 位。
这是我的主要装配文件,我不太确定这是哪个型号。大,也许?没有单个段大于 64k,所以可能不会。有一个堆栈段和一个代码段,但有几个数据段。所有这些都是 public 并在构成程序的整个程序集文件中共享。
theStack SEGMENT STACK
db 64 dup ('THESTACK') ;512 byte stack
theStack ENDS
varData SEGMENT PUBLIC
INCLUDE const.inc ;global constants
PUBLIC fCntr
fCntr db 0 ;A frame counter used to delay animations.
varData ENDS
frame SEGMENT PUBLIC
db scrArea dup (247d) ;64,000 byte frame buffer
frame ENDS
field SEGMENT PUBLIC
db 65535 dup ('F') ;64k buffer that holds up to 32,768 tile indexes
field ENDS
sprites SEGMENT PUBLIC
db 65535 dup ('S') ;64k buffer for animated spites
sprites ENDS
code SEGMENT PUBLIC
EXTRN SET_VGA_256:PROC,INIT_DISK_VARS:PROC,INIT_AREA:PROC,CALC_DELAY:PROC
EXTRN HANDLE_INPUT:PROC,UPDATE_SPRITES:PROC,DRAW_SPRITES:PROC
EXTRN DRAW_FIELD:PROC,WRITE_FRAME:PROC,FRAME_DELAY:PROC,EXIT2DOS:PROC
EXTRN DBG:PROC
assume cs:code,ds:varData
main PROC
start:
mov ax, varData
mov ds, ax ;Load the variable segment into ds
cld ;ensure that string operations auto-increment
call SET_VGA_256 ;Set the video mode to 320x200 256 colors.
call INIT_DISK_VARS ;Setup hard drive access variables
call INIT_AREA ;Build the area into memory from data files
call CALC_DELAY ;calculate the frame delay using the RTC
LOOP_TILL_ESC:
call HANDLE_INPUT ;Handle user input.
call UPDATE_SPRITES ;bounds check then move the sprites
call DRAW_FIELD ;draw the tiles that make up the play field
call DRAW_SPRITES ;draw all currently visible sprites
call WRITE_FRAME ;Write the frame buffer to video memory.
inc fCntr ;increment the frame counter
call FRAME_DELAY ;delay for the specified number of milliseconds
cmp bp, 1 ;Was the Esc key pressed?
jne LOOP_TILL_ESC ;If not, loop back through the main program.
call EXIT2DOS ;If so, return to DOS.
main ENDP
code ENDS
END start
这里有一个简单的程序,如果使用 .386 就会中断。它应该用粉红色像素填充屏幕,但它却挂在黑屏上并使 DOSBox 崩溃。
.MODEL SMALL
.386
theStack SEGMENT STACK
db 64 dup ('THESTACK')
theStack ENDS
code SEGMENT PUBLIC
assume cs:code,ds:varData
main PROC
start:
mov ax, varData
mov ds, ax ;Load the variable segment into ds
cld ;ensure that string ops auto-increment
xor ah, ah ;select set video mode function
mov al, 13h ;320x200 256 colors
int 10h ;video mode set
mov di, 0a000h
mov es, di
xor di, di ;es:di -> vga pixel buffer
mov ah, 64d
mov al, ah ;ah & al -> pink color index byte
mov cx, 32000d ;writing 32,000 words
rep stosw ;fill the screen with pink pixels
ESC_LOOP:
in al, 60h
cmp al, 1
jne ESC_LOOP ;delay till escape key is pressed
mov ax, 40h
mov es, ax ;access keyboard data area via segment 40h
mov WORD PTR es:[1ah], 1eh ;set the kbd buff head to start of buff
mov WORD PTR es:[1ch], 1eh ;set the kbd buff tail to same as buff head
;now the keyboard buffer is cleared.
xor ah, ah ;select video mode function
mov al, 3 ;select 80x25 16 colors
int 10h ;restore VIDEO back to text mode
mov ah, 4ch ;Terminate process DOS service
xor al, al ;Pass 0 to ERRORLEVEL
int 21h ;Control returns to DOS
main ENDP
code ENDS
END start
我错误地认为 .386 指令属于汇编文件的最顶部。实际上它属于代码段定义。在堆栈定义上方包含 .386 会导致堆栈的对齐类型为 DWORD 而不是 WORD,这意味着像 push ax
这样的堆栈操作被视为 push eax
。这破坏了与期望 16 位堆栈的代码的兼容性,这就是我的程序崩溃的原因。
在使用 .386 之后定义任何其他段之前需要使用 .8086 指令。
您对问题的描述并非 100% 正确,但您的 "pink" 样本来源是充分解释问题的好例子。
.MODEL
指令与.CODE, .CONST, .DATA, .DATA?, .FARDATA, .FARDATA?, .STACK
简化段指令。
所以在工作方式和 16b DOS 可执行文件中使用它们的一种方法是这样的:
.MODEL SMALL
.386
.STACK 100h
.DATA
x DB 1
.CODE
start:
mov ax,@DATA
mov ds,ax
movzx eax,BYTE PTR [x]
mov ah,4Ch
int 21h
END start
仅使用简化指令,.CODE
将定义名为 _TEXT
的代码段,这是 16b 实模式代码段(由于 .386
指令被放置 after .MODEL SMALL
指令).
您的 "pink" 示例不使用简化的段指令,而是使用完整的段指令,然后您必须在代码段定义中指定它用于实模式,就像在下一个固定源中一样,它将填满屏幕首先是粉红色(使用 16b 寄存器),然后是一些青色键(在实模式下使用 32b 寄存器)。
我必须将 USE16
添加到 code SEGMENT
指令中,以正确设置它,然后生成的 32b 指令以正确的方式为 16b 实模式添加前缀(即不同于32b 保护模式)。
我进一步测试了当你将显式代码段定义与简化的 .CODE
指令混合时会发生什么,令人惊讶的是(对我来说)最终的 .exe 有两个代码段,即使 .MODEL SMALL
模型... 所以"dotCode"段中的测试过程只能通过FAR调用才能到达。至少 .CODE
段被正确分配为 16b 段,因此生成的程序集按预期工作。
固定示例(使用 TASM 4.1 + TLINK 测试,仅 运行 带有文件名且没有选项,应生成 ASM -> OBJ -> EXE 文件):
.MODEL SMALL
.386
theStack SEGMENT USE16 STACK
db 64 dup ('THESTACK')
theStack ENDS
; test of simplified code segment directive
.CODE
testDotCode PROC
mov eax,12345678h
retf
testDotCode ENDP
ENDS
code SEGMENT USE16 PUBLIC
assume cs:code, ss:theStack
main PROC
call FAR PTR testDotCode ; test code inside simplified code segment definition
; with experiment I find out, that even with ".MODEL SMALL" the TASM+TLINK will
; put the testDotCode subroutine into new "_TEXT" code segment!
; So only FAR call + retf works to access it.
cld ; ensure that string ops auto-increment
mov ax, 13h ; select set video mode function: 13h 320x200 256 colors
int 10h ; video mode set
mov di, 0a000h
mov es, di ; es = VRAM segment
; original 16b test code - fill screen with pink
xor di, di ;es:di -> vga pixel buffer
mov ah, 64d
mov al, ah ;ah & al -> pink color index byte
mov cx, 32000d ;writing 32,000 words
rep stosw ;fill the screen with pink pixels
; wait for any key
xor ah,ah
int 16h
; 32b test code to validate ".386" setup success in real mode
xor di, di ; es:edi -> vga pixel buffer
mov eax, 34343434h ; eax = 4x cyan color
mov ecx, 320*200/4 ; full screen fill
rep stosd ; fill the screen with pink pixels
; wait for any key
xor ah,ah
int 16h
; restore text mode (3)
mov ax,3 ; select video mode: text 80x25 16 colors
int 10h
mov ax,4C00h ; terminate DOS process with 0 ERRORLEVEL
int 21h
main ENDP
code ENDS
END main
我主要使用此网页作为这些详细信息的来源:http://www.c-jump.com/CIS77/ASM/Directives/lecture.html#D77_0070_code_directive
并且此答案并未详尽替换正确的 TASM/MASM 文档,不幸的是,只是(希望完整)解释导致您出现问题的原因。