[ บทความ : เรียนรู้ z80 ] ตอนที่ 5 เรื่อง เรียนรู้คำสั่งของ Z80 |
รู้จักคำสั่งของ Z80
ตอน คำสั่ง LD
หลังจากหายหน้าหายตาไปนานกับ บทความเกี่ยวกับ Z80 กลับมาคราวนี้เรามาเริ่มกันที่เรื่อง คำสั่ง LD กันเลยครับ ... LD เป็นคำสั่งที่ทำหน้าที่โอนค่าจากที่หนึ่งไปเก็บเอาไว้ ณ อีกที่หนึ่ง รูปแบบหลักๆ ของ LD เป็นดังนี้LD ปลายทาง,ต้นทาง LD (ปลายทาง), ต้นทาง LD ปลายทาง, (ต้นทาง)นั่นหมายความว่า เมื่อเราเขียนเป็นสัญลักษณ์แทนการทำงาน ก็จะได้เป็นดังนี้ปลายทาง <- ต้นทาง (ปลายทาง) <- ต้นทาง ปลายทาง <- (ต้นทาง)หมายเหตุปลายทาง มักเป็นรีจิสเตอร์
ต้นทาง จะเป็นได้ทั้ง รีจิสเตอร์ และ ค่าคงที่ (ตัวเลข ที่เรากำหนด)
(ปลายทาง) หมายความว่า เราจะเอาค่าไปเก็บ ณ หน่วยความจำ
(ต้นทาง) หมายความว่า ข้อมูลที่เราต้องการนั้น อยู่ ในหน่วยความจำ
ตัวอย่าง 1 :
LD A, 15หมายความว่า A <- 15 หรือ นำค่า 15 ไปเก็บในรีจิสเตอร์ A
ตัวอย่าง 2 :
LD C,Bหมายความว่า C <- B หรือ นำค่าที่เก็บอยู่ในรีจิสเตอร์ B มาเก้บในรีจิสเตอร์ C
ตัวอย่าง 3:
LD A,(1FFFh)หมายความว่า A <- (1FFFh) หรือ นำค่าที่เก้บเอาไว้ในหน่วยความจำ ณ ตำแหน่ง 1FFFh (h = เป็นการบอกว่าเป็นค่าแบบ ฐานสิบหก) มาเก็บไว้ใน รีจิสเตอร์ A
ตัวอย่าง 4:
LD (2FF1h),Aหมาายความว่า (2FF1h) <- A หรือ นำค่าที่เก้บเอาไว้ในรีจิสเตอร์ A มาเก็บไว้ ณ หน่วยความจำตำแหน่ง 2FF1h
ตัวอย่าง 5:
LD (IX+5),Aหมายความว่า (IX+5) <- A หรือ นำค่าที่เก้บเอาไว้ในรีจิสเตอร์ A ไปเก็บไว้ในหน่วยความจำ ณ ตำแหน่งที่ IX + 5 เช่น IX เก็บค่า 1FF0h นั่นหมายความว่า ตำแหน่งหน่วยความจำที่จะได้ ก็จะเป็น 1FF0h+5 = 1FF5h นั่นเอง
คราวนี้เรามาดูรูปแบบการเขียนกันดีกว่าครับ
กลุ่ม LD ที่ทำงานกับข้อมูลขนาด 8 บิต
กลุ่ม LD ที่ทำงานกับข้อมูลขนาด 16 บิต
หมายเหตุ
r,r' คือ รีจิสเตอร์ตัวใดก็ได้ดังต่อไปนี้ A, B, C, D, E, H และ L.
p,p' คือ รีจิสเตอร์ตัวใดก็ได้ดังต่อไปนี้ A, B, C, D, E, IX(Hi-Byte) และ IX (Lo-Byte)
q,q' คือ รีจิสเตอร์ตัวใดก็ได้ดังต่อไปนี้ A, B, C, D, E, IY(Hi-Byte) และ IY (Lo-Byte)
dd คือ pair register อันได้แก่ BC,DE,HL และ SP
qq คือ pair register ดังต่อไปนี้ BC, DE, HL และ AF
d คือ ค่าตัวเลขขนาด 16 บิต
n คือ ค่าตัวเลขขนาด 8 บิต
nn คือ ค่าตัวเลขขนาด 16 บิต
Flag note 1
S,Z,F5,F3 flag is set according to the result of the operation (เปลี่ยนแปลงตามผลของการทำงาน)
H,C flag = 0
P/V flag -> The interrupt flip-flop 2 is copied
โปรแกรมตัวอย่างของเราจะเป็นดังนี้ครับ ...
; ; Filename : LDex.asz ; Author : Supachai Budsaratij (raek@se-ed.net) ; Date : Dec 5, 2000 ; Hardware : ET-Board V6 (Z80 Mode) ; ; INCL "etv6.inz" ; Inlude header for ET-V6 DD EQU 0AH EE EQU 20H NN EQU 0A000H N EQU 0AH ORG UMEM_ORG ; Start at UMEM_ORG main ; --- My code here LD A, 0A0h LD B, 0ACh LD C, A LD (BC), A LD (HL), B LD (IX + DD), C LD (IX + DD), N LD (IY + DD), B LD (IY + DD), N LD (NN), A LD (NN), BC LD A, (BC) LD A, (IX + DD) LD A, (IY + DD) LD A, (NN) LD A, A LD IX, (NN) LD IX, NN LD IY, (NN) LD IY, NN ; ---End of program HALT ENDหลังจากนั้นก็ทำการแปลด้วยคำสั่งต่อไปนี้ครับ
az80 LDex.asz -l LDex.lst -o LDex.hexเมื่อเราคอมไพล์โปรแกรมเสร็จเราก็จะได้ไฟล์มาอีก 2 ไฟล์ คือ .HEX สำหรับ Load เข้า บอร์ด กับ .LST สำหรับบอกรายละเอียดทั้งหมด ... ตัวอย่างของไฟล์ .LST เป็นดังนี้ครับ
; ; Filename : LDex.asz ; Author : Supachai Budsaratij (raek@se-ed.net) ; Date : Dec 5, 2000 ; Hardware : ET-Board V6 (Z80 Mode) ; ; INCL "etv6.inz" ; Inlude header for ET-V6 ; ; filename : etv6.inz ; assembler : az80 ; author : Supachai Budsaratij (raek@se-ed.net) ; hardware : et-board 6 (Z-80 mode) ; date : October 12,2000 ; ; --- MEMORY ; 8000 UMEM_ORG EQU 08000h ; User RAM start bdff UMEM_END EQU 0BDFFh ; User RAM end c000 XMEM_ORG EQU 0C000h ; Expand RAM end dfff XMEM_END EQU 0DFFFh ; Expand RAM end ; --- I/O ; 0000 S8255_PA EQU 00h ; System 8255 port A 0001 S8255_PB EQU 01h ; System 8255 port B 0002 S8255_PC EQU 02h ; System 8255 port C 0003 S8255_CT EQU 03h ; System 8255 control port ; 0020 U8255_PA EQU 20h ; User 8255 port A 0021 U8255_PB EQU 21h ; User 8255 port B 0022 U8255_PC EQU 22h ; User 8255 port C 0023 U8255_CT EQU 23h ; User 8255 control port ; 004d SCN_INP EQU 4Dh ; SCN2681 - Input port (Switch) 004e SCN_SEO EQU 4Eh ; SCN2681 - Set output port (LED) 004f SCN_REO EQU 4Fh ; SCN2681 - Reset output port (LED) ; 0060 CLCD_WC EQU 60h ; Character LCD write command 0061 CLCD_RC EQU 61h ; Character LCD read command 0062 CLCD_WD EQU 62h ; Character LCD write data 0063 CLCD_RD EQU 63h ; Character LCD read data ; 0064 GLCD_WC1 EQU 64h ; Graphics LCD write command (Page 1) 0065 GLCD_RC1 EQU 65h ; Graphics LCD read command (Page 1) 0066 GLCD_WD1 EQU 66h ; Graphics LCD write data (Page 1) 0067 GLCD_RD1 EQU 67h ; Graphics LCD read data (Page 1) ; 0068 GLCD_WC2 EQU 68h ; Graphics LCD write command (Page 2) 0069 GLCD_RC2 EQU 69h ; Graphics LCD read command (Page 2) 006a GLCD_WD2 EQU 6Ah ; Graphics LCD write data (Page 2) 006b GLCD_RD2 EQU 6Bh ; Graphics LCD read data (Page 2) ; 000a DD EQU 0AH 0020 EE EQU 20H a000 NN EQU 0A000H 000a N EQU 0AH 8000 ORG UMEM_ORG ; Start at UMEM_ORG 8000 main ; --- My code here 8000 3e a0 LD A, 0A0h 8002 06 ac LD B, 0ACh 8004 4f LD C, A 8005 02 LD (BC), A 8006 70 LD (HL), B 8007 dd 71 0a LD (IX + DD), C 800a dd 36 0a 0a LD (IX + DD), N 800e fd 70 0a LD (IY + DD), B 8011 fd 36 0a 0a LD (IY + DD), N 8015 32 00 a0 LD (NN), A 8018 ed 43 00 a0 LD (NN), BC 801c 0a LD A, (BC) 801d dd 7e 0a LD A, (IX + DD) 8020 fd 7e 0a LD A, (IY + DD) 8023 3a 00 a0 LD A, (NN) 8026 7f LD A, A 8027 dd 2a 00 a0 LD IX, (NN) 802b dd 21 00 a0 LD IX, NN 802f fd 2a 00 a0 LD IY, (NN) 8033 fd 21 00 a0 LD IY, NN ; ---End of program 8037 76 HALT 8038 ENDเอาล่ะ เราจะมา debug โปรแกรมกันครับ ... เริ่มต้นจากการโหลดโปรแกรมเข้าบอร์ด ดังรูปต่อไปนี้เลยครับ
... เริ่ม trace ล่ะนะ ... พิมพ์คำสั่งว่า T 8000 แล้วกด enter แล้วจะมีการแสดงผลดังรูปด้านล่าง
จากการ trace ครั้งแรก ทำให้รู้ว่า คำสั่งที่ ได้ทำไปนั้น คือ LD A,0A0H ผลจากการทำงาน ทำให้ A เก็บค่า A0 เอาไว้ (ดูจาก AF เป็น A000 นั่นหมายความว่า A เป็น A0h และ F เป็น 00h) มาดูคำสั่งถัดไปดีกว่าครับ ... ให้กด enter ต่อไปแล้วจะมีการแสดงผลออกมาดังนี้
คราวนี้เราได้ทำการประมวลผลคำสั่ง LD B,0ACh ทำให้ผลลัพธ์ของรีจิสเตอร์ B เป็น AC
เมื่อ trace อีกครั้ง คราวนี้คำสั่งที่กำลังทำงานเป็นคำสั่ง LD C.A พอจะเดาออกไหมครับว่าค่าของ C จะเปลี่ยนไปเป็นแบบ A ทั้งนี้เพราะเราสั่งให้นำค่าที่เก็บเอาไว้ใน A มาใส่ใน C ... ดังจะเห็นได้จากรูปด้านบนค่า BC จะเป็น ACA0 ซึ่งหมายความว่า B มีค่าเป็น ACh และ C มีค่าเป็น A0h หลังจากนั้นก็ให้กด Enter เพื่อดูผลของคำสั่งต่อไป
จากรูปด้านบนจะเห็นว่า คำสั่งที่กำลังทำงานนั้น คือ LD (BC),A ซึ่งมีความหมายว่า จะนำค่าที่เก็บใน A ซึ่งก็คือ A0h ไปเก็บเอาไว้ในหน่วยความจำ ณ ตำแหน่งที่กำหนด เอาไว้ในรีจิสเตอร์ BC ซึ่งก็คือ ACA0h ดังนั้น ผลการทำงานของคำสั่งนี้จะทำให้ หน่วยความจำ ณ ตำแหน่ง ACA0hเก็บค่า A0h เอาไว้ ... เพื่อให้เห็นผลของการทำงานจริง .. ให้เรากด ESC เพื่อออกจากการ trace แล้วกดคำสั่ง D ACA0,ACAF เพื่อสั่งให้ โปรแกรมมอนิเตอร์ แสดงข้อมุลของหน่วยความจำตำแหน่ง ACA0 ถึง ACAF ออกมาทางหน้าจอ ... ซึ่งผลจะออกมาเป็นดังนี้
จะเห้นว่า ที่ ACA0 มีค่าที่เก็บเอาไว้เป็น A0 ซึ่งเป็นการยืนยันว่า โปรแกรมของเราทำงานได้ถูกต้อง ... แต่ที่เราจะต้องระวัง ก็คือ เราสามารถเขียนโปรแกรม และเขียนลบ ข้อมูลในตำแหน่ง 8000h ขึ้นไปเท่านั้น เพราะ 0000h ถึง 7FFFh นั้นเป็นของโปรแกรมมอนิเตอร์ และถ้าจะให้ดี เราควรเก็บ ข้อมูล เอาไว้ ณ ตำแหน่งที่อยู่สูงกว่า โปรแกรมที่เราเขียน ซึ่งจากตัวอย่างด้านบน เราเข้าถึง หน่วยความจำ ณ ตำแหน่ง ACA0 ซึ่งอยู่ด้านบนของโปรแกรมมอนิเตอร์ และโปรแกรม ที่เราเขียน แต่ถ้าเราไปแก้ไขข้อมูล ณ ตำแหน่งที่เป็นของ โปรแกรมของเรา จะทำให้เกิดข้อผิดพลาดในการทำงานของโปรแกรมได้ แต่ถ้าเราเข้าไปแก้ไขข้อมูล ณ ตำแหน่งของ โปรแกรมมอนิเตอร์ เราก็จะไม่สามารถเปลี่ยนแปลงค่าใดๆ ได้เลย ทั้งนี้เพราะ โปรแกรมมอนิเตอร์เป็นหน่วยความจำแบบ ROM เราจึงไม่สามารถแก้ไขมันได้ นั่นเอง ....
ส่วนถ้าเราจะ run คำสั่งต่อไปก็ให้สั่งว่า T 8006 เพื่อทำการ trace คำสั่ง ณ หน่วยความจำตำแหน่ง 8006 ต่อไป ...แล้วกด enter ไปเรื่อยๆ พร้อมทั้งคอยสังเกตว่า คำสั่งนั้นๆ มีผลกับรีจิสเตอร์อย่างไร บ้าง ... นี่ล่ะครับ style ของการเขียนโปรแกรมด้วยภาษาแอสเซมบลี ... จากตัวอย่างนี้เมื่อทำงานไปเรื่อยๆ สุดท้ายก็จะหยุดทำงานด้วยคำสั่ง HALT ซึ่งทำให้ Z80 หยุดการทำงานทั้งหมด ...
เป็นอย่างไรบ้างครับ คำสั่ง LD ... หวังว่าคงเข้าใจการใช้คำสั่งนี้กันแล้วนะครับ คราวหน้าเราจะมาดูคำสั่ง ที่เกี่ยวกับ stack ครับ ...