คำสั่ง Assembly (MASM) พื้นฐานที่ถามกันบ่อย
ในบทความนี้เราจะมาดู คำสั่งในภาษา Assembly สำหรับ MASM ที่ถูกถามกันบ่อยๆ เพื่อเพิ่มความเข้าใจของเราให้มากขึ้น ยกตัวอย่างเช่น ADDR กับ OFFSET ต่างกันอย่างไร และจะใช้เมื่อไหร่? ใน Assembly มีตัวแปรแบบ Structure หรือไม่และ If-then-else ในภาษา Assembly เป็นอย่างไร เราจะได้มาทำความเข้าใจกันในบทความนี้ครับ ไปดูต่อกันเลย
ADDR and OFFSET
OFFSET operator จะคืนค่ากลับมาเป็นตำแหน่งของตัวแปร ใช้สำหรับระบุตำแหน่งของตัวแปรแทนที่จะเป็นค่าของตัวแปร:
MyVar db 77h ; byte-sized variable called MyVar initialised to 77h .code mov eax, MyVar ; copies 77h into eax mov ebx, offset MyVar ; copies memory address where 77h stored into ebx
นอกจากนั้น OFFSET ยังสามารถใช้ในการส่งค่าตำแหน่งของตัวแปรไปยังฟังก์ชั้นต่างในการใช้งานคำสั่ง invoke มันใช้งานได้เฉพาะตัวแปรที่เป็น Global ที่ประกาศไว้ในส่วนของ .data หรือ .data? มันไม่สามารถใช้กับตัวแปรที่ประกาศตอนใช้งาน proc ที่ใช้คำสั่ง LOCAL ซึ่งไม่มีค่า offset เพราะมันถูกสร้างบน stack ขณะรันโปรแกรม (runtime) ADDR operator จะมาแก้ปัญหานี้ มันถูกใช้โดยเฉพาะกับฟังก์ชัน invoke เพื่อส่งค่าตำแหน่งของตัวแปรไปยังฟังก์ชัน สำหรับ Goble variables มันจะแปลงเป็นคำสั่ง push ซึ่งเหมือนกันกับที่ OFFSET ใช้
push GlobalVar
อย่างไรก็ตาม สำหรับตัวแปรแบบ Local ADDR จะแปลงคำสั่งเป็น:
lea eax, LocalVar ; load effective address of LocalVar into eax
(สิ่งสำคัญที่ต้องจำไว้ก็คือ เมื่อมีการใช้ addr กับตัวแปรแบบ local eax จะถูกใช้งาน ดังนั้นค่า eax จะไม่ว่างให้ใช้งานสำหรับ Procedure นั้นๆ) lea eax, LocalVar โดยปกติแล้วจะเท่ากับคำสั่ง mov eax, offset LocalVar แต่ใช้งาน 1 cpu cycle mov...offset เตรียมไว้สำหรับกรณีอื่น ที่ไม่ใช้ตัวแปรแบบ local Square Brackets [] โดยปกติจะใช้แสดงค่า (เนื้อหา) ของตัวแปรที่ตรงกับตำแหน่งที่ระบุ อย่างไรก็ตาม Syntax ของ MASM แตกต่างเล็กน้อยจาก assembler เจ้าอื่นๆในกรณีนี้ ใน MASM คำสั่งทั้งหมดนี้จะสร้างคำสั่งออกมาเหมือนกัน
mov eax,1 mov eax,[1] mov eax, DWORD PTR 1 mov eax, DWORD PTR [1]
MyVariable และ [MyVariable] ทั้งสองหมายถึงค่าของ MyVariable โปรแกรมเมอร์หลายคนใช้ square bracket กับตัวแปรเพื่ออ้างถึงค่าของตัวแปร ซึ่งทำให้โค้ดอ่านง่าย และง่ายต่อการพอร์ตไป assemblers อื่น จากที่กล่าวมาข้างต้น offset MyVariable และ addr MyVariable ทั้งสองหมายถึงตำแหน่งของ MyVariable เมื่อใช้งานกับรีจีสเตอร์ Square brackets ทำให้เกิดควมแตกต่างเพื่อแสดงถึงตำแหน่งของหน่วยความจำ (Memory address)
mov ebx,eax ; copies the value in eax into ebx. mov ebx,[eax] ; copies the value at the memory address in eax into ebx mov [ebx],eax ; copies the value in eax into memory at the address in ebx.
The PTR operator (h3) PTR operator เป็นตัวบอกให้ assembler ทราบถึงขนาดของข้อมูลในแต่ในขณะนั้น ที่ซึ่งขนาดของข้อมูลไม่สามารถได้มาโดยอัตโนมัติ ตัวอย่างเช่น:
MOV [eax], 0
นี่เป็นคำสั่งที่ไม่สามารถใช้งานได้ เพราะว่าตัวจัดการกับหน่วยความจำ [eax] ไม่สามารถบ่งบอกถึงขนาดหน่วยความจำได้ ถ้าเราต้องการคัดลอกค่า 0 (zero) ไปยังหน่วยความจำแบบ BYTE ที่ตำแหน่ง [eax] เราต้องใช้ BYTE PRT เพื่อบอกให้ assembler รู้ว่าเราจะใช้หน่วยความจำเท่าำไหร่ เพื่อให้เข้าใจมากขึ้นไปดูตัวอย่างกัน
mov bl, [eax]
Compiler รู้ว่า 'bl' เท่ากับ 8 bits เรากำลังสั่งให้ processor เก็บค่าที่อยู่ในตำแหน่งที่เก็บอยู่ใน eax (eax เก็บตำแหน่งของ Memory ปลายทาง) conpilre จะรู้ขอบเขตคือ 8 bits โดยทราบจากรีจีสเตอร์ 'bl' ที่ปลายทาง จึงทำให้ไม่มีปัญหา
mov bl, byte ptr [eax]
คำสั่งนี้เหมือนกันทุกประการกับข้างบน ซึ่งมันไม่จำเป็นแต่เพื่อให้อ่านง่ายยิ่งขึ้น ทำไมเราต้องใช้ BYPT PTR หรือ WORD PTR ตามด้วย "at-the-address-of" ([...]) statement? คำตอบดูได้จากต่อไปนี้:
mov [ebx], 3
นี่เป็นการหนีเสือปะจรเข้สำหรับ compiler คำสั่งข้างบนหมายถึง "move 3 ไปยังตำแหน่งหน่วยความจำที่เก็บอยู่ใน ebx" compiler มีการคิดเป็น bits และต้องรู้ว่า 3 เป็น BYTE, WORD, หรือ DWORD เป็นต้น มันไม่มีทางรู้ว่าจะใช้กี่หน่วยความจำกี่ bits เพื่อใช้ในการเก็บค่าที่ได้ เพื่อทำให้มันกระจ่างมากขึ้น ใช้ BYTE PTR ในการเก็บค่า 3 ไว้ในหน่วยความจำ 1 Byte ในตำแหน่งที่กำหนด เช่นเดียวกันกัีบ WORD PTR จะใช้หน่วยความจำ 2 Bytes ในการเก็บค่า 3 :
mov byte ptr [ebx], 3
Structures การประกาศตัวแปรประเภท Structure:
Example STRUCT field1 field2 field3 Example ENDS
การกำหนด instance ของตัวแปรแบบ structure ที่ยังไม่มีการกำหนดค่าเริ่มต้น:
.data? MyStruct1 Example
หรือ กำหนดค่าเริ่มต้นของตัวแปร การเข้าถึง Field แบบโดยตรง:
mov eax,Mystruct2.field2 ; copy 67 into eax
การเข้าถึง Field ทางอ้อม:
mov ebx,offset MyStruct2 ;ebx contains pointer to MyStruct2 structure
Syntax1:
mov eax,[ebx].Example.field2
Syntax2:
mov eax,[ebx.Example.field2]
Syntax3:
mov eax,[Example ptr eax].field2
Syntax4:ใข้ ASSUME directive:
assume eax:ptr Example mov [eax].field2 assume eax:nothing ; always remember to un-assume when finished.
High-Level Constructs in MASM
MASM อนุญาตให้เราใช้งานคำสังภาษา C ระดับสูง เพื่อทำให้โค้ดเราอ่านง่าย assembler จะแปลงโค้ดเหล่านี้เป็น คำสั่ง assembly ทั่วไป ก่อนที่จะแปลงไปเป็นออปเจ็คโค้ด(object code)

Relational Operators
เราสามารถใช้คำสั่งในการเปรียบเทียบ สำหรับ .IF, .REPEAT, และ .WHILE ได้เหมือนกับที่ใช้ในภาษา C MASM จะแปลงคำสั่งเหล่านี้เป็น compare, test, และ conditional jump ซึ่งประกอบไปด้วย:
เงื่อนไขที่ไม่มี operators test สำหนับ nonzero (พวกที่ไม่เท่ากับ 0) เหมือนกันที่ใช้ใน C:
.WHILE (x) is the same as .WHILE (x != 0) - while x is nonzero
.WHILE (!x) is the same as .WHILE (x == 0) - while x is zero
เราสามารถใช้ flag names (ZERO?, CARRY?, OVERFLOW?, SIGN?, and PARITY?) เป็นตัวดำเนินการได้ด้วยเหมือนกัน ตัวอย่างเช่น .WHILE (CARRY?) ค่าของ carry flag เพื่อเป็นตัวกำหนดผลของเงื่อนไข
Register Preservation
Win32 ฟังก์ชั่น ที่เราเรียกใช้งาน (call) จะทำการจอง "segment registers" และรีจีสเตอร์ EBX, EDI, ESI และ EBP ตรงกันข้าม ECX และ EDX จะยังไม่มีเก็บค่าจนกว่าจะ Return จาก Win32 ฟังก์ชั่น EAX จะไม่ถูกจอง ซึ่งบ่อยครั้งมันจะถูกใช้ในการเก็บค่าที่คืนค่ามาจากฟังก์ชั่น (return value) ดังนั้น ถ้าเรามีข้อมูลที่สำคัใน EAX, ECX หรือ EDX เราจะต้องเก็บมันไว้ที่อืนก่อน (เช่น เก็บมันไว้บน Stack) ที่จะเรียกใช้งาน API ฟังก์ชั่น
เช่นเดียวกับ ถ้าเราใช้ EBX, EDI, ESI หรือ EBP ใน Callback ฟังก์ชั่น อย่าลืมที่จะคืนค่า(Restore) ของพวกมันก่อนที่จะส่งการควบคุมให้ก้ับ windows นี่ไม่ได้หมายความว่า เราไม่สามารถใช้รีจีสเตอร์เหล่านี้ได้ เพี่ยงแต่ให้เรามั่นใจว่าเราได้คืนค่า (Restore) พวกมันก่อนที่จะส่งการควบคุมกลับไปยัง windows
เรียบเรียงจาก: Win32 Assembler Coding โดย Goppit