在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位寄存器而不更改默认段定义大小的主要内容,如果未能解决你的问题,请参考以下文章

访问段寄存器 MASM

定义和调用接收不同数据类型的宏时指令操作数大小错误。使用 masm32

masm - 字母大小写转换

MASM 程序集将 8 位寄存器移动到 16 位寄存器(即 mov cx,ch)[重复]

汇编语言 EAX怎么得到EIP的地址?

32 位 MASM 模式与 64 位有何不同?