在MASM中使用32位寄存器而不更改默认段定义大小
Posted
tags:
篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了在MASM中使用32位寄存器而不更改默认段定义大小相关的知识,希望对你有一定的参考价值。
我的代码对于8086和80286处理器来说太慢了,所以我决定在我的实模式代码中使用32位寄存器和指令。
我知道我真正需要做的就是用66h作为单独指令的前缀,但如果你没有在汇编文件的最顶层包含.386指令,MASM就不接受386个寄存器。
在这之后,我发现我的程序不再运行,即使我没有使用任何386寄存器。它挂在黑屏上,然后DOSBox崩溃。这种行为通常表示我的程序中的堆栈崩溃和内存损坏。
在MASM 5.10(我使用的版本)的文档中,我发现了以下信息:“如果在.MODEL指令之前使用.386指令,则段定义定义32位段。如果要启用80386处理器16在比特段中,你应该在.MODEL指令之后给出.386指令。“
我很确定这是我的问题,我需要包含一个.MODEL指令,以确保段保持16位。在使用.386指令之前,我尝试在我的主程序集文件中包含所有列出的.MODEL指令(文档将它们称为最常见的模型)。它们都会产生错误,我认为这可能是因为我没有将.MODEL指令包含在构成我程序的其他十几个汇编文件中。我只想继续使用默认的.MODEL,无论它是什么。
到目前为止,我从未需要使用.MODEL指令,文档没有提到默认情况下使用的指令,或者在使用.386时保持16位段保持不变。
.MODEL SMALL,.MODEL MEDIUM和.MODEL COMPACT都会产生许多链接器错误,如下所示:错误L2002:修正溢出在0016处的段CODE位置:1FA记录类型:48A8
.MODEL LARGE,和.MODEL HUGE组装和连接都很好,但几帧之后崩溃了我的程序,一些垃圾被转储到视频内存中,可能是堆栈崩溃。此外,我目前还没有在我的其他任何程序集文件中包含.MODEL指令。
我想要的是偶尔能够使用386个寄存器和指令,但我希望程序的行为与以往完全相同。处理所有段,例如16位。
这是我的主要汇编文件,我不太确定这是哪个型号。很大,也许?没有单个段大于64k,所以可能没有。有一个堆栈段和一个代码段,但有几个数据段。所有这些都是公共的,并在组成程序的整个程序集文件中共享。
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
您对问题的描述不是100%正确,但您的“粉红色”样本来源是充分解释它的好例子。
.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
指令之后放置了.MODEL SMALL
指令)。
您的“粉红色”示例不使用简化的段指令,而是使用完整的指令,那么您必须在代码段定义中指定它是用于实模式,如下一个固定源,它将首先用粉红色填充屏幕(使用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文档,只是(希望完整)解释导致您出现问题的原因。
我错误地认为.386指令属于程序集文件的最顶层。实际上它属于代码段定义。在堆栈定义之上包含.386会导致堆栈的对齐类型为DWORD而不是WORD,这意味着像push ax
这样的堆栈操作被视为push eax
。这打破了与期望16位堆栈的代码的兼容性,这就是我的程序崩溃的原因。
在使用.386之后定义任何其他段之前,需要使用.8086指令。
以上是关于在MASM中使用32位寄存器而不更改默认段定义大小的主要内容,如果未能解决你的问题,请参考以下文章
定义和调用接收不同数据类型的宏时指令操作数大小错误。使用 masm32