[ บทความ : เรียนรู้ z80 ] ตอนที่ 18 เรื่อง การเขียนโปรแกรมย่อย และปิดฉาก Z80 |
เรียนรู้ Z80 ตอนที่ 18
การเขียนโปรแกรมย่อย และปิดฉาก Z80
สวัสดีครับ หายหน้าหายตากันไป 2 สัปดาห์ มาคราวนี้ เป็นบทความตอนสุดท้ายของ Z80 กันแล้วนะ ... ที่ผ่านๆ มาเป็นอย่างไร กันบ้างครับ หวังว่าผู้อ่านคงได้ประโยชน์จากบทความชุดนี้กันนะครับ ... มาเข้าเรื่องของเรากันดีกว่า ...
ในบทความที่ผ่านๆ มานั้น เราได้เขียนโปรแกรมไปหลายโปรแกรมแล้วใช่ไหมครับ ... ผู้อ่านสังเกตเจออะไรบ้างไหมครับ ... รู้สึกไหมว่าโปรแกรมที่เราเขียนนั้นเป็นโปรแกรมที่จบในตัวของมันเอง มีงานที่ไม่ซับซ้อน ต้องการให้มันทำงานอะไรก็เขียนเพิ่มเข้าไป ... ซึ่งเราจะต้องเขียนใหม่กันทั้งหมด (บางท่านอาจจะใช้วิธีการคัดลอก จากสิ่งที่มีอยู่แล้วก็ไม่ว่ากันครับ) ... มาลองดูตัวอย่างโปรแกรม ประกอบบทความกันก่อนครับ
; ; Filename : Z00.asz ; Author : Supachai Budsaratij (raek@se-ed.net) ; Date : May 27, 2001 ; Hardware : ET-Board V6 (Z80 Mode) ; ET-Board V5 ; ET-Board V4 ; ET-Board V3.5 NewPower ; CP-jr180 Plus ; INCL "etv6.inz" ; Include header for ET-V6 ; INCL "etv5.inz" ; Include header for ET-V5 ; INCL "etv4.inz" ; Include header for ET-V4 ; INCL "etv35.inz" ; Include header for ET-V3.5 ; INCL "jr180.inz" ; Include header for CP-jr180 Plus P_DIGIT EQU S8255_PA P_SEGM EQU S8255_PB P_LEDFLAG EQU 6 ORG UMEM_ORG ; Start at UMEM_ORG main LD SP, UMEM_END loop LD A,P_LEDFLAG ; Display data (start) OUT (P_DIGIT),A ; LD A,0 ; OUT (P_SEGM),A ; Display data (stop) LD HL,8000h ; Delay (start) LD BC,1 ; delay1 SBC HL,BC ; JP NZ,delay1 ; Delay (stop) LD A,P_LEDFLAG ; Display data (start) OUT (P_DIGIT),A ; LD A,08h ; OUT (P_SEGM),A ; Display data (stop) LD HL,8000h ; Delay (start) LD BC,1 ; delay2 SBC HL,BC ; JP NZ,delay2 ; Delay (stop) LD A,P_LEDFLAG ; Display data (start) OUT (P_DIGIT),A ; LD A,80h ; OUT (P_SEGM),A ; Display data (stop) LD HL,8000h ; Delay (start) LD BC,1 ; delay3 SBC HL,BC ; JP NZ,delay3 ; Delay (stop) JP loop ENDจากโปรแกรมตัวอย่าง จะพบว่าโปรแกรมมีส่วนที่ซ้ำกันอยู่ ด้วยกัน 2 ส่วน คือ ส่วนสำหรับการหน่วงเวลา กับ ส่วนที่ทำหน้าที่ เลือก และส่งข้อมูลออกทางพอร์ต LED ..
ข้อดี (advantage) ของการเขียนโปรแกรมด้วยวิธีนี้ก็คือ เราสามารถเขียนโปรแกรม ต่อเติมไปได้เรื่อยๆ โดย ไม่จำเป็นจะต้องออกแบบโปรแกรมมาก่อนล่วงหน้า
ข้อเสีย (disadvantage)แต่ถ้าโปรแกรมของเราโตขึ้นเรื่อยๆ เราจะประสบปัญหาเกี่ยวกับ การหาข้อผิดพลาด และการแก้ไขโปรแกรม (เพราะโปรแกรมจะมีจำนวนบรรทัดเยอะมากๆ และจะต้องอ่านโปรแกรมตั้งแต่ต้น เพื่อหาว่าผิดพลาด จุดใด) แถม การเขียนโปรแกรมแบบนี้ยังกินพื้นที่หน่วยความจำเยอะด้วย (เพราะปริมาณโค้ดเยอะ เวลาคอมไพล์ก็ได้ ไฟล์ผลลัพธ์ขนาดใหญ่ขึ้น)
ด้วยเหตุดังกล่าว จึงมีการออกแบบให้ผู้เขียนโปรแกรม สามารถแยกโปรแกรมออกเป็นส่วนๆ เพื่อลดความซ้ำซ้อนของโปรแกรม ดังนั้น ถ้าเราแยกส่วนที่เรียกซ้ำกันมากๆ ออกมาเป็นโปรแกรมย่อย นอกจากจะทำให้เราแก้ไขโปรแกรมได้ง่ายขึ้น, อ่านโปรแกรมได้ง่ายขึ้น แล้ว ยังทำให้ไฟล์ HEX ที่ได้นั้น มีขนาดเล็กลงด้วย
โปรแกรมย่อย
โปรแกรมย่อย คือ ส่วนของโปรแกรมที่เราเขียนนี่ล่ะครับ แต่เป็นการเขียนโดยออกแบบมาเพื่อ ลดจำนวนบรรทัดของโปรแกรม และลดความซ้ำซ่อนของโค้ดที่เราเขียน นั่นตือ ถ้าเรามีส่วนของโปรแกรมที่เขียนเหมือนๆกัน (ซ้ำๆกัน) เราก็เอาส่วนของโปรแกรมนั้น แยกออกมาเป็นโปรแกรมย่อยซะ (นั่นเป็นหลักการง่ายๆ) ... ซึ่งถ้าเราต้องการใช้งาน เมื่อไร ก็ค่อยเรียกโปรแกรมย่อยส่วนนั้นๆ ขึ้นมาใช้งาน ... ก่อนอื่น มาดูรูปแบบคำสั่งที่เกี่ยวกับการเขียนโปรแกรมย่อยกันก่อนครับตาราง 18-1 คำสั่งที่เกี่ยวข้องกับการเขียนโปรแกรมย่อย
Instruction Source/Target Flag Operation CALL nn ไม่มีผลกับ flag (SP-1) <- PCH
(SP-2) <- PCL
PC <- nnCALL cc, nn ไม่มีผลกับ flag if cc is true then
(SP-1) <- PCH
(SP-2) <- PCL
PC <- nn
end ifRET - ไม่มีผลกับ flag PCL <- (SP)
PCH <- (SP+1)RET cc ไม่มีผลกับ flag if cc is true then
PCL <- (SP)
PCH <- (SP+1)
else
continue
end ifRETI - ไม่มีผลกับ flag Return from interrupt RETN - ไม่มีผลกับ flag return from nonmaskable interrupt RST pp ไม่มีผลกับ flag (SP-1) <- PCH
(SP-2) <- PCL
PCH <- 0
PCL <- pp
โดยที่ pp คือ 00h, 08h, 10h, 18h, 20h, 28h, 30h, หรือ 38h
หมายเหตุ
cc คือ NZ, Z, NC, C, PO, PE, P หรือ M
NZ คือ nonzero
Z คือ zero
NC คือ noncarry
C หคือ carry
PO คือ parity odd
PE คือ parity even
P หคือ sign positive
M คือ sign negative
คราวนี้เรามาออกแบบโปรแกรมย่อยกันก่อนดีกว่า ... จากโปรแกรมด้านบน เราจะเขียนโปรแกรมขึ้นมาใหม่โดยใช้หลักการ ของโปรแกรมย่อย ... เราจะได้โปรแกรมย่อยดังต่อไปนี้
1. delay สำหรับหน่วงเวลาผังงานของโปรแกรมย่อย delay
2. display สำหรับเลือกพอร์ต และส่งข้อมูลออกทางพอร์ตของ LED โดยมีข้อกำหนดว่า ก่อนเรียกโปรแกรมย่อยนี้ เราต้อง ใช้ register D สำหรับ กำหนดข้อมูลที่จะส่งมาแสดงผล
delay LD HL,8000h LD BC,1 dloop SBC HL,BC JP NZ,dloop RETผังงานของโปรแกรมย่อย display
display LD A,P_LEDFLAG OUT (P_DIGIT),A LD A,D OUT (P_SEGM),A RETผังงานของโปรแกรมหลัก
main LD SP, UMEM_END loop LD D,0 CALL display CALL delay LD D,08h CALL display CALL delay LD D,80h CALL display CALL delay JP loopจากผังงานจะได้ว่า โปรแกรมของเราเริ่มต้นด้วยการกำหนดค่า SP ใหม่ หลังจากนั้นกำหนดให้ D=0 แล้วเรียกโปรแกรมย่อย display ขึ้นมาทำงาน ตัว Z80 ของเราก็จะกระโดดไปทำงานในโปรแกรมย่อย display เมื่อมันทำงานจนเสร็จ ก็จะกลับมาที่โปรแกรมหลัก ... แล้วก็เจอคำสั่งให้เรียกโปรแกรมย่อย delay ขึ้นมาทำงาน คราวนี้ Z80 ก็ไปทำงานที่ delay ตามที่เราสั่ง เมื่อเสร็จการทำงานใน delay ก็จะกลับมาที่โปรแกรมหลักอีกครั้ง ... และจะทำอย่างนี้ไปเรื่อยๆ ... (พอจะเข้าใจไหมครับ :D)
มาดูโปรแกรมเต็มๆ กันครับ
; ; Filename : Z01.asz ; Author : Supachai Budsaratij (raek@se-ed.net) ; Date : May 27, 2001 ; Hardware : ET-Board V6 (Z80 Mode) ; ET-Board V5 ; ET-Board V4 ; ET-Board V3.5 NewPower ; CP-jr180 Plus ; INCL "etv6.inz" ; Include header for ET-V6 ; INCL "etv5.inz" ; Include header for ET-V5 ; INCL "etv4.inz" ; Include header for ET-V4 ; INCL "etv35.inz" ; Include header for ET-V3.5 ; INCL "jr180.inz" ; Include header for CP-jr180 Plus P_DIGIT EQU S8255_PA P_SEGM EQU S8255_PB P_LEDFLAG EQU 6 ORG UMEM_ORG ; Start at UMEM_ORG main LD SP, UMEM_END loop LD D,0 CALL display CALL delay LD D,08h CALL display CALL delay LD D,80h CALL display CALL delay JP loop ;-- display module (start) display LD A,P_LEDFLAG OUT (P_DIGIT),A LD A,D OUT (P_SEGM),A RET ;-- display module (stop) ;-- delay module (start) delay LD HL,8000h LD BC,1 dloop SBC HL,BC JP NZ,dloop RET ;-- delay module (stop) ENDเป็นอย่างไรบ้างครับกับบทความ Z80 (แบบง่ายๆ หรือขั้นเริ่มต้นนั่นแหละครับ) ... มาถึงจัดนี้ เรายังขาดเรื่องที่ซับซ้อน ไป บ้าง เช่น การทำงานของระบบ interrupt กับ การ interface กับอุปกรณ์ภายนอก (แถม timing diagram ก็ไม่ได้กล่าวอย่างละเอียด) ... ซึ่งทั้ง 2 ส่วนนี้ผม คาดหวังเอาไว้ว่า ผู้อ่านน่าจะศึกษาเพิ่มเติม ได้ไม่ยาก ... และสุดท้ายนี้ขอให้ผู้ที่สนใจจะเขียนโปรแกรมด้วย Z80 ... จงประสบความสำเร็จ ในการเขียนโปรแกรมด้วยครับ ... สำหรับบทความนี้ คงจบเพียงเท่านี้ครับ ... สวัสดีครับ