ป้ายเลื่อน


WelCom To www.Kiss-Hack.blogspot.com สังคม IT สังคม Hacker

หมวดหมู่

วันเสาร์ที่ 9 พฤศจิกายน พ.ศ. 2556

ภาษา Assembly คืออะไร? และ การเขียนโปรแกรมด้วยภาษา Assembly





ภาษาแอสเซมบลี (อังกฤษ: Assembly Language) หมายถึง ภาษาที่ใช้ในการเขียนโปรแกรมภาษาหนึ่งซึ่งจะทำงานโดยขึ้นกับรุ่นของไมโครโพรเซสเซอร์ หรือ "หน่วยประมวลผล" (CPU) ของเครื่องคอมพิวเตอร์
การใช้ภาษาแอสเซมบลีจำเป็นต้องผ่านการแปลภาษาด้วยคอมไพเลอร์เฉพาะเรียกว่า แอสเซมเบลอร์ (assembler) ให้อยู่ในรูปของรหัสคำสั่งก่อน (เช่น .OBJ) โดยปกติ ภาษานี้ค่อนข้างมีความยุ่งยากในการใช้งาน และการเขียนโปรแกรมเป็นจำนวนบรรทัดมากมากกว่า เมื่อเปรียบเทียบกับการใช้ภาษาระดับสูง เช่น ภาษา C หรือภาษา BASIC แต่จะทำให้ได้ผลลัพธ์การทำงานของโปรแกรมเร็วกว่า และขนาดของตัวโปรแกรมมีขนาดเนื้อที่น้อยกว่าโปรแกรมที่สร้างจากภาษาอื่นมาก จึงนิยมใช้ภาษานี้เมื่อต้องการประหยัดเวลาทำงานของเครื่องคอมพิวเตอร์ และเพิ่มประสิทธิภาพของโปรแกรม
เนื่องจากตัวคำสั่งภายในภาษาอ้างอิงเฉพาะกับรุ่นของหน่วยประมวลผล ดังนั้นถ้ามีการเปลี่ยนแปลงไปใช้กับหน่วยประมวลผลอื่นหรือระบบอื่น (เช่น หน่วยประมวลผล x86 ไม่เหมือนกับ z80) จะต้องมีการปรับแก้ตัวคำสั่งภายในซึ่งบางครั้งอาจไม่สามารถปรับปรุงแก้ไขได้อย่างสมบูรณ์

         การพัฒนางานทางด้านไมโครคอนโทรลเลอร์ด้วยภาษา Assembly นั้น ถือว่ายังเป็นที่นิยมอยู่มาก
ในบ้านเรา เรียกได้ว่าเป็นส่วนใหญ่เลย ในขณะที่ต่างประเทศนั้น ภาษา Assembly จะนิยมรอง
ลงมาจากภาษา C ที่ใช้กันเป็นอันดับหนึ่ง อาจจะเป็นเพราะว่าความคุ้นเคย หรือเพราะความตรง
ไปตรงมาของภาษา Assembly ที่เป็นจุดเด่นอย่างหนึ่งก็ได้ ไม่ว่าจะอย่างไรก็ตาม การเข้าถึง
ภาษา Assembly ได้นั้น ถือว่าเป็นการเรียนรู้และเข้าใจได้ลึกซึ้งที่สุด และถ้ารู้จักนำมาประยุกต์
ใช้งานให้เหมาะสมแล้ว ก็จะเป็นประโยชน์ต่อการพัฒนาเทคโนโลยีต่าง ๆ เป็นอย่างมาก
สำหรับชิพตระกูล MCS-51 นั้น (ขอย้ำ ... ตระกูลครับ ... ไม่ใช่เบอร์ 51 ... แต่มากันเป็นร้อย ๆ เบอร์)
จากประสบการณ์ที่ได้สะสมมา พอจะสรุปเป็นประเด็นต่าง ๆ ได้ 10 ประเด็น ที่นักพัฒนาทั้งหลาย
ควรจะจดจำไว้ให้แม่น เพื่อจะช่วยให้การพัฒนาเป็นไปได้อย่างไม่ติดขัด ... ดังนี้ครับ

1. Internal RAM พื้นที่ยุทธศาสตร์
MCS-51 ถูกออกแบบให้มี Internal RAM ภายใน 128 Byte (เบอร์ 8051) หรือ 256 Byte (เบอร์ 8052)
และนี่เป็นความตั้งใจของผู้ผลิตที่จะให้ใช้พื้นที่ส่วนนี้เพื่อการทำงานต่าง ๆ คือเป็น Working Area ไม่ว่าจะ
เป็นส่วน Register, Stack, และ Buffer ชุดคำสั่งเกือบจะทั้งหมดจะกระทำกับ Internal RAM นี้ ถ้าเรา
จะพยายามใช้ส่วนของ External RAM เป็นพื้นที่ในรูปแบบ Working Area ก็จะเป็นเรื่องที่ยุ่งยากเกินไป
ส่วน External RAM เหมาะสำหรับการเก็บข้อมูลแบบต่อเนื่อง หรือทำเป็น Buffer จำนวนมาก ๆ เท่านั้น
เพราะเนื่องจากมีคำสั่งที่ใช้งานได้ไม่กี่คำสั่ง เช่น MOVX A,@DPTR ในส่วน Internal RAM นั้น เรา
สามารถใช้งานโดยตรงได้โดยไม่ต้องใช้ Register ให้ยุ่งยากเลย ตัวอย่างเช่น

MOV COUNT,#18H
LOOP: ...
...
...
DJNZ COUNT,LOOP

ในที่นี้ COUNT คือชื่อที่อ้างถึงพื้นที่ Internal RAM ที่กำหนดจากคำสั่ง EQU หรือ DS อีกที สังเกตุว่าเรา
ใช้งานได้เสมือนกับเป็น Register R0-R7 เลย อีกประเด็นหนึ่งที่เกี่ยวกับ Internal RAM ก็คือ การอ้าง
ถึงพื้นที่ RAM ตั้งแต่ Address 80-FFH (เบอร์ 8052) ซึ่งมี Address ซ้อนทับกับส่วน SFR 80-FFH ด้วย
จะต้องแยกด้วยคำสั่งที่ใช้งาน ถ้าเป็นกรณีอ้างถึง RAM ที่ตำแหน่ง A0H จะต้องใช้คำสั่งแบบ Indirect ดังนี้

MOV R0,#0A0H
MOV A,@R0

แต่ถ้าเป็นกรณีอ้างอิงถึง SFR ที่ตำแหน่ง A0H เช่นกัน จะต้องใช้คำสั่งแบบ Direct ดังนี้

MOV A,0A0H

2. ระบบ Stack และการเรียงข้อมูล
ระบบ Stack ของ MCS-51 จะเป็นแบบนับขึ้น โดยมีค่าเริ่มต้นหลังจาก Reset เป็น 07H เสมอ ถ้ามีการใช้
คำสั่ง PUSH หรือ CALL ค่า Stack จะเพิ่มขึ้นก่อน แล้วจึงเก็บข้อมูลลงไป ซึ่งก็จะเป็น Address 08H เป็น
ตัวแรก ระบบ Stack สามารถโยกย้ายไปที่ใดก็ได้ใน Internal RAM ซึ่งรวมไปถึงส่วน Address 80-FFH
ด้วย โดยจะไม่ปะปนกับส่วน SFR แต่อย่างใด ... ส่วนการเรียงลำดับข้อมูลขนาด 16 บิทในคำสั่งต่าง ๆ
จะเรียงจาก High ไป Low เสมอ เช่นการใช้คำสั่ง LJMP ไปที่ Address 87FFH จะแปลงเป็น Op-Code
คือ 02H,87H,FFH

3. ชื่อ Register หรือ Bit ... กำหนดให้ถูกกับการใช้งาน
ใน MCS-51 จะมีมุมมองจากจุดต่าง ๆ กัน เพราะฉะนั้นจะมีชื่ออ้างอิงที่ต่างกันด้วย ทั้งนี้การที่ผู้ผลิตชิพ
ออกแบบไว้อย่างนี้ ก็เพื่อให้ชุดคำสั่งใช้ได้อย่างมีประสิทธิภาพโดยไม่สับสน ชื่อจะมีอยู่ 3 แบบ คือ

ชื่อเฉพาะ เช่น A, R2, C(Carry), DPTR
ชื่อ Bit เช่น INT1, T0, T1, CY(Carry)
ชื่อ SFR (Special Function Register) เช่น ACC, B, DPH, P1, PSW, TCON

ชื่อทั้ง 3 กลุ่มนี้ บางตัวก็คือตัวเดียวกัน แต่มองจากคนละมุม เช่น A และ ACC , C และ CY ทั้งนี้เพื่อ
ให้เข้ากับชุดคำสั่งของแต่ละประเภท ตัวอย่างเช่น คำสั่ง PUSH จะกำหนดให้ตามด้วยชื่อแบบ Direct
เพราะฉะนั้นจะต้องใช้เป็น PUSH ACC จะใช้เป็น PUSH A ไม่ได้ ชื่อ Direct ในที่นี้ยังหมายถึงชื่อ
ที่เรากำหนดเองก็ได้จากคำสั่ง DS หรือ EQU ด้วย อีกตัวอย่างเช่น คำสั่ง JB bit,rel ถ้าเราใช้เป็น
JB CY,rel ก็จะทำงานเหมือนคำสั่ง JC rel เช่นกัน โดยมี Op-Code ที่แตกต่างกัน ทั้งนี้ CY จะเป็นชื่อ
ที่มองในฐานะของ Bit Address ส่วน C จะเป็นชื่อที่เป็นรูปแบบของคำสั่งไปเลย เราจะต้องเข้าใจ
คำสั่งแต่ละคำสั่งให้ชัดเจน จึงจะสามารถอ้างอิงได้ถูกต้องตามขบวนท่า นอกจากนี้ ชื่อ SFR ยังมี
ความสามารถในการใส่จุดหลังชื่อได้อีก ซึ่งจะทำให้เรียกใช้งานได้ในหลายรูปแบบ ตัวอย่างเช่น
P3.3 จะเหมือนกับ INT1 (คือ SFR ชื่อ P3 บิทที่ 3) , PSW.7 จะเหมือนกับ CY นั่นเอง

4. Boolean Processor ต้องใช้ให้คุ้ม
MCS-51 มีความสามารถในเรื่องบิทที่ดีมาก ซึ่งจะต้องนำมาใช้ประโยชน์ให้คุ้มค่า โดยเฉพาะอย่างยิ่ง
ในเรื่องของการกำหนด Flag ต่าง ๆ พื้นที่ใน Internal RAM ส่วนหนึ่งคือ 20-2FH (16 Byte,128 Bit)
และพื้นที่ SFR ที่มี Address ลงท้ายด้วย 0 และ 8 จะสามารถอ้างอิงถึงในระดับบิทได้ เปรียบเสมือนมี
Register 1 บิท ซึ่งนำมาเปรียบเทียบได้ , move ได้ , กำหนดให้เป็น 0 หรือ 1 ได้ รวมทั้งทำการ Jump
ตามค่าบิทนั้น ๆ ได้ด้วย การใช้งาน MCS-51 โดยไม่ได้ใช้คำสั่งเกี่ยวกับบิทเลย คงจะเป็นเรื่องแปลก
ทีเดียว

5. ใช้ Registor ขนาด 16 บิทอย่างเหมาะสม
หลายคนคงรู้สึกอึดอัดใจกับ MCS-51 ในเรื่องการอ้างอิง Address แบบ 16 บิท ซึ่งมี DPTR เพียงตัวเดียว
และยังทำการ Increment ได้อย่างเดียว โดยไม่สามารถ Decrement ได้ ในเรื่องนี้ถ้าเราใช้ External
RAM เพื่อการเก็บข้อมูลอย่างเหมาะสม ก็จะไม่รู้สึกถึงปัญหานี้แต่อย่างใด MCS-51 ได้ออกแบบโดยให้
ใช้งานที่ Internal RAM เป็นหลัก เพราะฉะนั้นไม่ต้องคิดมาก พยายามใช้งานให้สอดคล้องตามที่เขาออก
แบบมา ... สบายมาก

6. อักษรที่ต้องจำให้ขึ้นใจ (# และ @)
ในภาษา Assembly ของ MCS-51 อักษรทั้ง 2 ตัวนี้จะพบบ่อย ๆ และมักจะทำให้ผู้เริ่มสับสนได้พอสมควร
อักษร # จะใช้แทนค่าคงที่ เช่น คำสั่ง MOV A,#40H คือการนำค่า 40H เก็บไว้ใน Register A ถ้าไม่ได้ใส่
อักษร # ไว้ก็จะเป็น MOV A,40H ซึ่งจะมีการทำงานไปคนละเรื่องเลย คือการนำค่าจาก Internal RAM
ตำแหน่งที่ 40H ไปไว้ใน Register A ส่วนอักษร @ จะใช้ในความหมายของ In-direct คือการอ้างถึง
หน่วยความจำผ่านทาง Register อีกที เช่น MOV A,@R0 คือการนำค่าใน Internal RAM ที่ถูกชี้ด้วย R0
มาเก็บไว้ใน Register A หรือเช่น MOVX A,@DPTR คือการนำค่าใน Data Memory ที่ถูกชี้ด้วย DPTR
มาเก็บไว้ใน Register A

7. คำสั่งที่อาจจะสับสนได้
คำสั่งของ MCS-51 หลายคำสั่งที่ให้ความหมายไม่เหมือนกับคำสั่งของชิพตระกูลอื่น ๆ (โดยเฉพาะ Z80)
ซึ่งอาจจะทำให้สับสนได้ (ใครทำให้ใครสับสนก็ไม่รู้) พอจะรวบรวมได้ดังนี้ ...

คำสั่ง RLC และ RL หมายถึงการ Rotate Left แบบผ่าน Carry และไม่ผ่าน Carry ตามลำดับ
(Z80 จะให้ความหมายตรงข้ามกันเลย)

คำสั่ง JNZ และ JZ หมายถึงการกระโดดไป เมื่อ A ไม่เท่ากับ 0 และ A เท่ากับ 0 ตามลำดับ
สำหรับ MCS-51 จะตรวจสอบจากค่า A โดยตรง คือไม่ได้มี Zero flag เหมือน Z80 เพราะฉะนั้น
จะใช้ที่จุดใด ต้องแน่ใจว่าก่อนหน้านั้นมีการกระทำกับ Register A เสียก่อน

คำสั่ง SUBB หมายถึงคำสั่งลบเลข โดยใน MCS-51 จะสนใจค่า Carry ด้วยเสมอ คือถ้าเป็นการ
เริ่มต้นลบ จะต้องทำการ Clear Carry (CLR C) ก่อนเสมอ

คำสั่ง CJNE หมายถึงการ Compare ซึ่งจะทำการกระโดดไป เมื่อค่าไม่เท่า แต่ถ้าจะกระทำการ
เปรียบเทียบในเชิงค่ามากกว่า,น้อยกว่า ก็ทำได้เช่นเดียวกัน โดยการให้กระโดดไปยังบรรทัดต่อไป
และใช้คำสั่ง JNC และ JC ได้ตามต้องการ (ไม่สามารถใช้ JNZ และ JZ ได้) ตัวอย่างเช่น

CJNE A,B,$+3 (อักษร $+3 เป็นการบอกให้ตัวแปลรับรู้ โดยอ้างถึงตำแหน่ง ณ คำสั่งนี้ บวกไปอีก 3 Byte)
JNC ... ให้กระโดดไปที่ ... ถ้า A < B
หรือ
JC ... ให้กระโดดไปที่ ... ถ้า A => B

8. Interrupt ... เลือกใช้ให้เหมาะสม
เนื่องจาก MCS-51 มีโครงสร้างการ Interrupt ให้ใช้จากแหล่งหลาย ๆ อย่าง จึงทำให้ผู้เขียนโปรแกรมมีทาง
เลือกวิธีการใช้ได้มากขึ้นด้วย อย่างไรก็ตาม ใน Interrupt Routine จะต้องพยายามทำให้กระชับ และทำงาน
อย่างรวดเร็วที่สุดเท่าที่จะทำได้ ทั้งนี้ในระหว่างที่ทำงานอยู่ใน Interrupt Routine นี้ ถ้าเกิดมีกรณีการ
Interrupt ซ้อนกันอีกครั้ง ระบบจะไม่ตอบสนองการ Interrupt นั้น ๆ จนกว่าจะจบการทำงานของ Routine
ที่มีกำลังทำอยู่ (คือเจอคำสั่ง RETI) การ Interrupt ซ้อนกัน 1 ครั้งจะไม่มีปัญหาใด ๆ กับระบบ เพราะ MCS-51
จะยังคงตอบสนองได้ครบถ้วน แต่ถ้ามีการ Interrupt ซ้อนกันมากกว่า 1 ครั้ง (หมายถึงจากแหล่งเดียวกัน)
ก็จะทำให้การตอบสนองผิดพลาดไปได้

9. P0,P1,P2,P3 ... ใช้เป็น Active Low ให้หมด
เมื่อเปิดไฟเข้าระบบ หรือเมื่อทำงาน Reset ตัว MCU แล้ว ขาสัญญานทั้ง 4 Port จะมีสถานะเป็น 1 เสมอ
เพราะฉะนั้น ถ้ามีการนำขาสัญญานไปขับวงจรใด ๆ แล้ว ต้องคำนึงเสมอว่าให้วงจรทำงานแบบ Active Low
คือทำงานเมื่อเป็น 0 เช่นถ้านำไปขับวงจร Relay วงจรนี้จะทำให้ Relay ON เมื่อเป็น 0 ด้วยการออกแบบ
ที่ถูกต้องนี้ จะช่วยให้งานมีความสมบูรณ์ในการใช้งานเป็นอย่างดี

10. ไมโครคอนโทรลเลอร์ ก็คือ ไมโครคอนโทรลเลอร์
ความหมายของข้อนี้ก็คือ ให้ไมโครคอนโทรลเลอร์ทำงานของมันอย่างที่ควรเป็น ถ้าจะทำระบบที่ซับซ้อนมาก ๆ
หรือมีการเก็บข้อมูลอย่างมหาศาล หรือมีความเร็วสูงแบบไม่เห็นฝุ่น บางทีอาจจะต้องทบทวนก่อนว่า ระบบ
ไมโครคอนโทรลเลอร์จะสามารถทำได้อย่างที่ต้องการหรือไม่ เพราะชื่อของมันก็บอกอยู่แล้วว่า สำหรับการ
Control หรือการควบคุมนั่นเอง การนำไปใช้กับระบบที่เกินขอบเขตของมัน อาจจะต้องแยกงานเป็นส่วน ๆ
และใช้ MCU หลาย ๆ ตัวช่วยกันทำงาน หรืออาจจะออกแบบให้ทำงานร่วมกับเครื่อง PC หรือถ้ายังไม่ไหว
ก็เปลี่ยนไปใช้ MCU ที่มีขนาดใหญ่ ๆ ตามไปด้วยเลยจะดีกว่า ...

ตัวอย่างโค้ดโปรแกรม

org 100h
; set video mode    
mov ax, 3     ; text mode 80x25, 16 colors, 8 pages (ah=0, al=3)
int 10h       ; do it!
; cancel blinking and enable all 16 colors:
mov ax, 1003h
mov bx, 0
int 10h
; set segment register:
mov     ax, 0b800h
mov     ds, ax
; print "hello world"
; first byte is ascii code, second byte is color code.
mov [02h], 'H'
mov [04h], 'e'
mov [06h], 'l'
mov [08h], 'l'
mov [0ah], 'o'
mov [0ch], ','
mov [0eh], 'W'
mov [10h], 'o'
mov [12h], 'r'
mov [14h], 'l'
mov [16h], 'd'
mov [18h], '!'
; color all characters:
mov cx, 12  ; number of characters.
mov di, 03h ; start from byte after 'h'
c:  mov [di], 11101100b   ; light red (1100) on yellow (1110)
    add di, 2 ; skip over next ascii code in vga memory.
    loop c
; wait for any key press:
mov ah, 0
int 16h
ret

ที่มา : http://th.wikipedia.org/