我的代码太慢了8086& 80286处理器所以我决定在我的实模式代码中使用32位寄存器和指令。
我知道我真正需要做的就是用66h作为单独指令的前缀,但如果你没有在汇编文件的最顶层包含.386指令,MASM就不接受386个寄存器。
这样做之后,我发现我的程序不再运行,即使我没有使用任何386寄存器。它挂在黑屏上,然后DOSBox崩溃。这种行为通常表示我的程序中存在堆栈崩溃和内存损坏。
在MASM 5.10(我使用的版本)的文档中,我发现了以下信息:“如果在.MODEL指令之前使用.386指令,则该段 定义定义了32位段。如果要使用16位段启用80386处理器,则应在.MODEL指令之后指定.386指令。“
我很确定这是我的问题,我需要包含一个.MODEL指令 将确保段保持16位。在使用.386指令之前,我尝试在我的主程序集文件中包含所有列出的.MODEL指令(文档将它们称为最常见的模型)。它们都会产生错误,我认为这可能是因为我没有将.MODEL指令包含在构成我程序的其他十几个汇编文件中。我只想继续使用默认的.MODEL,无论它是什么。
到目前为止,我从未需要使用.MODEL指令,文档没有提到默认情况下使用的指令,或者在使用.386时保持16位段不变。
.MODEL SMALL,.MODEL MEDIUM和.MODEL COMPACT都会产生许多链接器错误,如下所示:错误L2002:修复程序在0016处溢出溢出段CODE pos: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
答案 0 :(得分:3)
您对问题的描述不是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文档,不幸的是,只是(希望完整)解释导致您出现问题的原因。
答案 1 :(得分:0)
我错误地认为.386指令属于程序集文件的最顶层。实际上它属于代码段定义。在堆栈定义之上包含.386会导致堆栈的对齐类型为DWORD而不是WORD,这意味着像push ax
这样的堆栈操作被视为push eax
。这打破了与期望16位堆栈的代码的兼容性,这就是我的程序崩溃的原因。
在使用.386之后定义任何其他段之前,需要使用.8086指令。