[ บทความ : เรียนรู้ z80 ] ตอนที่ 9 เรื่อง คำสั่งเคลื่อนย้ายข้อมูล แบบกลุ่ม |
เรียนรู้ Z80 ตอนที่ 9
คำสั่งเคลื่อนย้ายข้อมูลแบบกลุ่ม
หายหน้าหายตาไปนานเหมือนเดิมครับ .... เหตุผลนั้นมากมายเลยครับ ตั้งแต่ เรื่องเรียน (ช่วงนี้งานโถมเข้ามาพร้อมๆ กัน เยอะมาก) , เรื่องที่สนใจ (กำลัง เขียน assembler/ compiler / monitor ที่เป็น virtual machine ของ stack machine ที่ผม ออกแบบเอาไว้ ...) แต่ที่แน่ ๆ ตอนนี้เรามาต่อเรื่องคำสั่งของ Z80 กันต่อดีกว่า ... มีนักศึกษาของผมหลายคนครับ ที่ถามว่า ทำไมผมยังจะมาสนใจ Z80 อยู่อีก ผมก็บอกว่า มันง่าย .. มันไม่มีอะไรพิเศษเลยสักอย่าง ... ไม่มีแม้กระทั่งคำสั่ง คูณ และ หาร ... ไม่มีพอร์ตในตัว .. ไม่มี RS232 ให้เราใช้งาน ... จะทำอะไรก็ทำเอาเอง ... สะใจดี .... ผมคิดเสมอครับ การเขียนโปรแกรมแบบลำบากๆ นั้นย่อมสร้างนักเขียนโปรแกรมฝีมือดีๆ ได้มากมาย ... ดังนั้น เวลาเราผ่านงานยากๆ ที่ทำโดยฝีมือของเราเองทั้งหมด พอเจอระบบ ที่มันสำเร็จรูป เราเองก็สามารถเรียนรู้ได้โดยง่าย ... ดีไม่ดี ยังสามารถดัดแปลง เพิ่มประสิทธิภาพให้ดียิ่งขึ้นได้อีกด้วย ... เพราะฉะนั้น ใครที่กำลังศึกษา Z80 ก็จงพยายามเรียนรู้ให้เข้าใจถึงหลักการออกแบบ และการทำงานของมันให้ดีนะครับ ... เพราะ เวลาที่เราเปลี่ยนไปใช้พวกไมโครคอนโทรลเลอร์ เราจะได้เข้าใจมันได้ง่ายขึ้น ... ความแตกต่างเรื่องภาษานั้นเป็นเรื่องปกติครับ โปรเซสเซอร์แต่ละรุ่นไม่มีความเหมือนกันอยู่แล้ว ... ยังไงก็ต้องเขียนกันใหม่อยู่ดี ... แต่ถ้าเราเข้าใจหลักการว่า เราจะทำงานนั้นๆ ได้อย่างไร หรือที่เรียกว่าเข้าใจการทำงาน และออกแบบการทำงานได้ด้วยตนเองนั้น (เรียกสั้นๆ แบบภาษาต่างประเทศว่า algorithm) เวลาที่เปลี่ยนภาษาแนวคิดนั้น ก็ยังเดิมๆ นั่นแหละครับ ... ยิ่งเราผ่านการเขียนโปรแกรมมามาก เวลานำไปใช้นี่แทบเรียกได้ว่า มาจากหัว ... ไม่ต้องเสียเวลาคิดออกแบบในกระดาษเลยล่ะครับ ... โอ ... เกริ่นเยอะพอควรแฮะ... มาเข้าเรื่องดีกว่าครับ
คำสั่งในคราวนี้เป็นเรื่องของการถ่ายโอนข้อมูลแบบเป็นกลุ่ม หรือบล็อก ... ถ้าจำกันได้ เราเคยศึกษาเรื่อง คำสั่งถ่ายโอนข้อมูลกันไปแล้ว นั่นก็คือ LD แต่ว่า คำสั่ง LD นั้น เป็นการถ่ายข้อมูลทีละ 1 ชุดเท่านั้น ... แต่ในคราวนี้เราจะกล่าวถึงการถ่ายโอนข้อมูล ในหน่วยความจำเป็นครั้งละจำนวนมากๆ เท่าที่เราจะสั่งการ ... คำสั่งที่เราจะใช้นั้นก็คือ LDD, LDDR, LDI, และ LDIR ซึ่งรูปแบบของคำสั่งจะเป็นดังนี้ครับ
ตาราง 9-1 ชุดคำสั่งสำหรับเคบื่อนย้ายข้อมูลเป็นกลุ่ม
Instruction Source/Target Flag Operation LDD ไม่มี C , Z , P/V , S (DE) <- (HL)
DE <- DE-1
HL <- HL-1
BC <- BC-1LDDR ไม่มี C , Z , S เหมือน LDD แต่ว่า คำสั่งนี้ จะทำการโอนข้อมูล ไปเรื่อยๆ จนกว่า BC จะเป็น 0 LDI ไม่มี C , Z , P/V , S (DE) <- (HL)
DE <- DE+1
HL <- HL+1
BC <- BC-1LDIR ไม่มี C , Z , S เหมือน LDI แต่ว่า คำสั่งนี้ จะทำการโอนข้อมูล ไปเรื่อยๆ จนกว่า BC จะเป็น 0 จะเห็นว่า คำสั่งทั้ง 4 นั้น จะทำงานกับหน่วยความจำเพียงอย่างเดียวเท่านั้น ... และความแตกต่างระหว่าง LDI กับ LDIR และ LDD กับ LDDR ก็คือว่า LDI , LDD นั้นจะทำงานเพียงครั้งเดียว ถ้าเราจะต้องการให้ทำงานต่อ เราต้องตรวจสอบค่า Flag เอาเอง แต่ LDIR, LDDR นั้น จะทำงานวนรอบไปเรื่อยๆ จนกว่าค่าของ BC จะเป็น 0
มาดูตัวอย่างโปรแกรมสำหรับ คำสั่งที่เกี่ยวกับ การแลกเปลี่ยนข้อมูล กันครับ ...
; ; Filename : TLDEx2.asz ; Author : Supachai Budsaratij (raek@se-ed.net) ; Date : Jan 18, 2001 ; Hardware : ET-Board V6 (Z80 Mode) ; CP-jr180 ; ET-Board V5 ; ET-Board V3.5 New Power ; ; ; INCL "etv6.inz" ; Inlude header for ET-V6 ; INCL "etv35.inz" ; Inlude header for ET-V3.5 INCL "jr180.inz" ; Inlude header for CP-jr180 ORG UMEM_ORG ; Start at UMEM_ORG main: ; --- My code here ; Load data from 9100h to 9001h keep into 9000h to 8F01h LD DE,09000h ; DE point to target address. LD HL,09100h ; HL point to source address. LD BC,0FFh ; BC = counter. LDD ; Load data from (HL) and keep into (DE). ; Do until the value of BC equal to 0. ; (This instruction to BC times.) ; LD DE,09000h LD HL,09100h LD BC,0FFh LDDR ; Similar to LDD but repeat until ; Z flag equal to 1 or BC equal to 0. ; ; ---End of program HALT ENDเมื่อเราทำการแปลคำสั่งเรียบร้อยแล้ว เราก็จะได้ไฟล์ .LST ดังนี้
; ; Filename : TLDEx2.asz ; Author : Supachai Budsaratij (raek@se-ed.net) ; Date : Jan 18, 2000 ; Hardware : ET-Board V6 (Z80 Mode) ; CP-jr180 ; ET-Board V5 ; ET-Board V3.5 New Power ; ; ; INCL "etv6.inz" ; Inlude header for ET-V6 ; INCL "etv35.inz" ; Inlude header for ET-V3.5 INCL "jr180.inz" ; Inlude header for CP-jr180 ; ; filename : jr180.inz ; assembler : az80 ; author : Supachai Budsaratij (raek@se-ed.net) ; hardware : CP-jr180 Plus ; date : Dec 5, 2000 ; ; Note : Must use jr180 debugger. ; ; --- MEMORY (Logical address) ; 8000 UMEM_ORG EQU 08000h ; User RAM start (10000h - Physical address) ffff UMEM_END EQU 0FFFFh ; User RAM end (17FFFh - Physical address) ; ; --- MEMORY (Physical address) 0000 P_UMEM_ORG EQU 10000h 7fff P_UMEM_END EQU 17FFFh 8000 P_XMEM_ORG EQU 018000h ; Expand RAM end ffff P_XMEM_END EQU 01FFFFh ; Expand RAM end ; --- I/O ; 0080 U8255_PA EQU 80h ; User 8255 port A 0081 U8255_PB EQU 81h ; User 8255 port B 0082 U8255_PC EQU 82h ; User 8255 port C 0083 U8255_CT EQU 83h ; User 8255 control port ; ; --- for Character LCD 00c0 CLCD_WC EQU 0C0h ; Write instruction to LCD 00c2 CLCD_WD EQU 0C2h ; Write data to CG or DD RAM 00c4 CLCD_RD EQU 0C4h ; Read busy flag and address ; --- for Graphics LCD 00c0 GLCD_WC EQU 0C0h ; Write instruction to LCD (Page1) 00c1 GLCD_WC2 EQU 0C1h ; Write instruction to LCD (Page2) 00c2 GLCD_WD EQU 0C2h ; Write data to CG or DD RAM (Page1) 00c3 GLCD_WD2 EQU 0C3h ; Write data to CG or DD RAM (Page2) 00c4 GLCD_RD EQU 0C4h ; Read busy flag and address (Page1) 00c5 GLCD_RD2 EQU 0C5h ; Read busy flag and address (Page2) ; 00e0 WATCH_DOG EQU 0E0h 8000 ORG UMEM_ORG ; Start at UMEM_ORG 8000 main: ; --- My code here ; Load data from 9100h to 9001h keep into 9000h to 8F01h 8000 11 00 90 LD DE,09000h ; DE point to target address. 8003 21 00 91 LD HL,09100h ; HL point to source address. 8006 01 ff 00 LD BC,0FFh ; BC = counter. 8009 ed a8 LDD ; Load data from (HL) and keep into (DE). ; Do until the value of BC equal to 0. ; (This instruction to BC times.) ; 800b 11 00 90 LD DE,09000h 800e 21 00 91 LD HL,09100h 8011 01 ff 00 LD BC,0FFh 8014 ed b8 LDDR ; Similar to LDD but repeat until ; Z flag equal to 1 or BC equal to 0. ; ; ---End of program 8016 76 HALT 8017 END 00c4 CLCD_RD 00c0 CLCD_WC 00c2 CLCD_WD 00c4 GLCD_RD 00c5 GLCD_RD2 00c0 GLCD_WC 00c1 GLCD_WC2 00c2 GLCD_WD 00c3 GLCD_WD2 7fff P_UMEM_END 0000 P_UMEM_ORG ffff P_XMEM_END 8000 P_XMEM_ORG 0083 U8255_CT 0080 U8255_PA 0081 U8255_PB 0082 U8255_PC ffff UMEM_END 8000 UMEM_ORG 00e0 WATCH_DOG 8000 mainส่วนรายละเอียดของไฟล์ฐานสิบหก ก็จะเป็นดังนี้ครับ
:1780000011009021009101FF00EDA811009021009101FF00EDB87613 :0080170168คราวนี้เรามาลอง debug กันนะครับ... อ๊ะๆ ... คราวนี้ผมขอใช้บอร์ด CP-jr180 V2 นะครับ เพราะไม่มีบอร์ด ET-V6 แถวๆ นี้เลย แต่โปรแกรมสามารถทำงานได้กับ ET-V6 ได้ด้วยการเปลี่ยนแปลงบรรทัด
; INCL "etv6.inz" ; Inlude header for ET-V6 ; INCL "etv35.inz" ; Inlude header for ET-V3.5 INCL "jr180.inz" ; Inlude header for CP-jr180ให้เป็น
INCL "etv6.inz" ; Inlude header for ET-V6 ; INCL "etv35.inz" ; Inlude header for ET-V3.5 ; INCL "jr180.inz" ; Inlude header for CP-jr180ก็เริ่มตั้งแต่โหลดโปรแกรมเลยนะครับ
หลังจากนั้นเราก็เริ่มสั่ง t 8000 เพอื่เริ่ม debug คำสั่งแรกเป็นการกำหนดให้ DE เป็น 9000h อันนี้จะเห็นว่า เรากำหนดล่ะนะว่า ปลายทางอยู่ที่ 9000h
ขั้นตอนต่อมาเราก็กำหนดให้ HL เป็น 9100h ซึ่งก็คือ กำหนดจุดเริ่มมต้นของข้อมูลเอาไว้ที่ 9100h
กำหนดจำนวนข้อมูลที่จะทำการถ่ายโอน ... โดยกำหนดผ่านทาง BC ... ตอนนี้เรากำหนดให้ทำ FFh รอบ
ทดลองเรียก LDD จะเห็นว่าค่าของ BC, DE, และ HL จะลดลง (ถ้าเป็น LDI ค่า DE กับ HL จะเพิ่มขึ้น)
อ้าว ... ทำไม มากำหนดให้ DE เป็น 9000h ใหม่ล่ะ ... อืม ... อืม ... อ๋อ ... คำสั่งก่อนหน้านี้เป็น LDD นี่นา ฬฬฬ มันก็ทำครั้งเดียวสิ ... อืม .. อืม .. เข้าใจๆ ล่ะ (เข้าใจอย่างผมไหมครับ)
ว่าแล้วก็กำหนดข้อมูลเริ่มต้นอยู่ที่ 9100h กันใหม่อีกรอบ
ทำ FFh ครั้งเหมือนเดิมนะ
เอาล่ะ เจ้า LDDR ทำงานได้ ... อ๊ะอ๋า .. คราวนี้ BC กลายเป็น 0 , DE เป็น 8F01 (9000-FF = 8F01) และ HL ก็เป็น 9001 (9100-FF = 9001) เห็นความแตกต่างแล้วใช่ไหมครับ
อ้าว จบโปรแกรมเสียแล้ว ...
เป็นอย่างไรบ้างครับ LDD กับ LDDR ส่วน LDI กับ LDIR นั้น ก็ขอให้ผู้อ่านลองเปลี่ยนคำสั่งดูเองล่ะกันครับ ... คราวหน้าเป็นคำสั่งอะไรดีหนอ ... อืม ... เอาคำสั่งค้นหาข้อมูลในหน่วยความจำกันก่อน แล้ว มาลองบวกลบ กันดูดีกว่า ... หลังจากนั้นก็ค่อยเป็นคำสั่งเงื่อนไข ... ต่อไปเริ่มสนุกแล้วนะครับ ... . เราก็จะว่าด้วย การติดต่อกับ I/O กันต่อ ส่วนที่เหลือ คงเป็นตัวอย่างจากบอร์ดรุ่นต่างๆ ล่ะนะครับ ... คิดว่า ถ้าหมด ภาคคำสั่ง แล้วผู้อ่าน เข้าใจหน้าที่ของคำสั่งต่างๆ เจ้าตัวอย่างโปรแกรมของบอร์ด ก็คงอ่านได้ไม่ยากจริงไหมครับ ...