MASM匯編常用基礎知識
☆學習及編譯環境:
??剛開始學習Masm匯編,有些東西好像跟教材不怎么一樣,有可能是Masm版本問題,我選擇的是MASM5.00,用Vim編輯代碼(有語法加亮),網上下載好像要分,VMware Workstation 16虛擬機鏈接如下:
??鏈接:https://pan.baidu.com/s/1pLR0YKzh7HGCpM0LZRw2Xw
??提取碼:yp6e
??2024年4月30日更新
??目前上述編譯環境已經優化,dos下的vim還是不怎么方便。而且vmware的虛擬環境并不是100%模擬舊的計算機硬件,部分bios調用都存在問題。因此我將虛擬環境換成PCem(模擬pentium 90,ASUS P55tvp4主板),通過安裝MS Client訪問真實pc的共享文件夾,映射一個驅動盤,在Dos下編譯,在Windows下通過VsCode編寫代碼(還可以用Ai插件),無論是代碼提示還是編碼查看,都方便很多。
☆以下是我在學習Masm過程中曾經遇到的問題,記錄如下:
??1、代碼中assume ss:stacksg,并且定義了stacksg,為什么還提示:Link:warning L4021:no stack segment沒有堆棧段呢,這里跟教材稍微不同,堆棧段需要這樣定義stacksg segment stack。
??segment的詳細語法:
????name SEGMENT align combine 'class"
????;align的值有:BYTE,WORD,PARA,PAGE
????;combine的值有:PUBLIC,COMMAN,STACK,MEMORY,AT address
?? ;定義在相同class中的segment會被連續加載,在內存中處于相鄰的位置,名字必須用單引號
??name ENDS
??★堆棧段最大的作用是指定堆棧的大小,所以一般都是定義類似如256 dup(?);如果沒有定義堆棧段,也可以在link時通過/stack:265指定也可以。
??2、數值常量進制后綴(不區分大小寫),B表示二進制,Q或O表示八進制,D表示十進制,H表示十六進制。不寫后綴用.radix設置。十六進制數如果第一個是字母,應該寫成數字0開頭,如B800H,必須寫成0B800H,否則會提示錯誤error A2006:undefined symbol xxx,編譯器把數字xxx做符號變量處理了。
??3、匯編代碼不區分大小寫,并通過分號(;)來表示代碼注釋。
??4、常用教材都是通過segName segment與segName ends來定義段,但在Masm5之后,如果沒有特殊的要求,可通過簡單的方式定義段:.model、.data、.code、.stack、.startup、.exit等,如:
.model small
.data
.stack
.code
.startup
.exit 0
end
??.model tiny指的是建立com文件,通常用small,編譯成exe可執行文件。
??.stack xxx,如果不設置,則stack默認為1024Byte,即1K。如.stack 100,即棧空間為100Byte。
??.exit后跟返回碼,如.exit xxx,與mov ax,4cxxh相同。.exit后跟end,且與.startup成對出現。
??5、group(組)指的是一組segment(段)的集合,語法是:name GROUP segment [[, segment]]...,在.MODEL語法下,默認將stack和data段定義在DGROUP下,方便進行訪問,只要在相應的模式下,Group的總大小不超過64K或4G,可在任何時候向組添加段成員,且添加的段成員順序并不影響最終在內存中的排列順序。
??6、ret除了可以返回以外,還可以控制SP的出棧數量,ret n的語法含義是:
1 pop ip
2 add sp,n
即返回調用出IP后,在彈出需丟棄的棧元素,通常用于子程序參數的棧傳遞。
??7、Lea和Offset作用都是獲取偏移地址,前者是Cpu指令,后者是偽指令(在編譯的時候計算,對CPU來說相當于立即數)。Offset只能對變量或標號進行計算,當不能對一般操作數進行計算,如:Lea ax,[bx+si]。但偽指令在編譯時計算,且后者是3字節內存尋址指令,所以用Offset程序運行速度較快。
??8、如果在WMware中安裝TRW2000進行匯編調試,如果無法彈出調試窗口,請修改相應的vmx文件,在文件中加入:
vmmouse.present="FALSE"
svga.maxFullscreenRefreshTick="5"
??9、在Bochs中使用Dos 6.22進行匯編調試時,不要在config.sys中加載emm386.exe,否則會卡死。
??10、MASM中常數的定義不能以字母開頭,如FFFFH要寫成0FFFFH。字符串是以ASCII碼存儲(就是說看成數字),如:mov ax,'he',定義的時候引號可以是單引號或雙引號,效果相同。
??11、系統裝載EXE文件時,會構建一個PSP(256字節)段,緊接著裝載程序,并把DS和ES初始化為PSP地址,所以,DS和ES需要手動寫代碼進行初始化。CS和SS通過“END 標號”和“Assume 標號 Stack”的方式分別在DOS頭中進行了初始化。當然,如果通過.Data的方式,Masm會替你生成一段代碼Mov DS,DGroup。當然,如果在.Model中如果Stack Distance定義為Nearstack的話,Masm會根據DOS頭(PE結構)重新定位SS和SP的初始化值:
1 mov dx, DGROUP
2 mov ds, dx
3 mov bx, ss
4 sub bx, dx
5 shl bx, 1 ; If .286 or higher, this is
6 shl bx, 1 ; shortened to shl bx, 4
7 shl bx, 1
8 shl bx, 1
9 cli ; Not necessary in .286 or higher
10 mov ss, dx
11 add sp, bx
12 sti ; Not necessary in .286 or higher
12
??12、在定義變量值為字符串時,db定義'abcd'和'a','b','c','d'存儲的方式相同;dw,dq,dt定義'ab'和'a','b'的存儲方式不同,且dw,dq,dt定義字符串'ab'的長度不能超過2個字符,'ab'的存儲方式是第一字節存儲'b',第二字節存儲'a',剩余部分用0補齊(如圖把ab看成一個數字)。
??13、MASM源碼中,如果進行直接尋址,必須加上段前綴,如mov ax,[3],這是不允許的,會被編譯成mov ax,0003,必須寫成:mov ax,ds:[3]。因為帶寄存器、標號或變量都可以確定偏移地址所在的段,但直接給地址無法確定是哪個段。
??14、masm5中如果要編寫com格式代碼,必須滿足2個條件,程序只包含1個segment,代碼中不含有段地址的引用,如mov ax,datasg;程序從0100h處開始執行。通過masm和link生成exe文件后(此文件并不能正常執行,因為沒有初始化ds的準確地址)。通過bin2exe(bin2exe hello.exe,hello.com)生成com文件后可正常執行,bin2exe在標配的系統中是沒有的,可安裝MS-DOS 6.22 Supplemental Kit(可在winpcworld下載)。
??示例代碼如下:
assume cs:_text,ds:_text,ss:_text
_text segment
org 100h
begin:
jmp start
msg db "Hello,world!",13,10
lmsg equ $ - msg
start:
mov ah,40h
mov bx,1
mov dx,offset msg
mov cx,lmsg
int 21h
mov ax,4c00h
int 21h
_text ends
end begin
??15、在simplified segment模式下,如果使用.model,則段寄存器通常與DGroup關聯,包括.stack;而不是像full segment模式下與DS關聯,stack與SS關聯。
??16、.bss和.data的區別:
??1)都屬于靜態內存分配,存放全局變量,bss(Block Started by Symbol)用于存放未初始化的變量。
??2)bss不占用exe文件空間,只存放占位符,有系統初始化。
??17、既然可以通過label:+ret的方式定義子程序,為什么還要proc+endp這樣的結構呢,因為前者需要手動定義label和ret的距離,如label是near,那么后面就是retn;如果label是far(通過label偽指令定義),則后面就是retf。如果通過proc的方式,只要定義時在proc后指明near或far,則ret指令會被自動替換成retn或retf。
??18、如果想使用386之后的新增指令,如果添加.386指令,masm 5默認寄存器為32位,解決辦法:
??如果用簡化段的方式,則在.model之后加入.386,而不是先寫.386然后寫.model;
??如果是完整段方式,則在segment 聲明后加use16。

浙公網安備 33010602011771號