โพรซีเดอร์ และมาโคร ในแอสเซมบลี้
Digital logic | OS | คำสั่งดอส | Batch | Debug | Assembly | GWBasic | Docker |
1. การเขียน procedure พิมพ์ A
- เขียน procedure นอก .code หรือ segment เหมือน macro ไม่ได้
- การเรียกใช้ procedure ต้องขึ้นต้นด้วยคำว่า call
- ก่อน endp ต้องสั่ง ret เพื่อกลับมายังบรรทัดที่ call
      .model  small
      .data           
      .code
x:    mov ax,@data   
      mov ds,ax
      call oho1    
y:    mov ah,4ch     
      int 21h
; === procedure ===
oho1 proc near      
      mov ah,2h     
      mov dl,41h      
      int 21h      
      ret
oho1 endp
      .stack  200h    
      end x

2. การเขียนมากกว่า 1 procedure
- procedure ถูกเรียกใช้ได้หลายครั้ง เช่นเดียวกับ macro
- สามารถประการ procedure ใต้ .code แล้วเรียกใช้ภายหลังได้
      .model  small
      .data           
      .code
x:    mov ax,@data   
      mov ds,ax
      call oho1   
      call oho2
      call oho1   
      call oho2      
y:    mov ah,4ch     
      int 21h
; === procedure ===
oho1 proc near      
      mov ah,2h     
      mov dl,41h      
      int 21h      
      ret
oho1 endp
oho2 proc near      
      mov ah,2h     
      mov dl,61h      
      int 21h      
      ret
oho2 endp
      .stack  200h    
      end x

3. การเขียน macro พิมพ์ A
- ประกาศ macro ก่อนเข้าโปรแกรม จะประกาศเหมือน procedure ไม่ได้
- เพียงพิมพ์ชื่อมาโคร ก็จะหมายถึงเรียกใช้ macro
helloa  macro
        mov     ah,2
        mov     dl,41h
        int     21h
        endm
; === end of macro ===
        .model  small
        .data
        .code
pmain   proc    far      
        push    ds      ; 1 of 5 line required for .exe
        mov     ax,0    ; 2 clear ax by xor  ax,ax
        push    ax      ; 3 send ax to stack
        mov     ax,@data
        mov     ds,ax
        helloa
        ret
pmain   endp
        .stack  200h    ; not required (if have, will not warning in link)
        end     pmain

4. การเขียนมากกว่า 1 macro
setproc macro
        push    ds      ; 1 of 5 line required for .exe
        mov     ax,0    ; 2 clear ax by xor  ax,ax
        push    ax      ; 3 send ax to stack
        mov     ax,@data
        mov     ds,ax
        endm	
prtout  macro
        mov     ah,9
        lea     dx,msg
        int     21h
        endm
; =============
; Main program
; =============
        .model  small
        .data
msg     db      'This is the program testing $'
        .code
pmain   proc    far     ; can not change far to near 
        setproc
        prtout
        ret
pmain   endp
        .stack  200h    ; not required
        end     pmain

5. การเขียน macro เพิ่มค่าให้กับ al ทีละ 1 แล้วพิมพ์ โดยเรียก macro 5 ครั้ง
love    macro
        mov     bl,01h
        add     al,bl
        mov     sum,al
        mov     dl,sum
        mov     ah,2
        int     21h
        endm
; =============
; Main program
; =============
        .model  small
        .data
n       db      30h
sum     db  ?
        .code
pmain   proc    far     ; can not change far to near 
        push    ds      ; 1 of 5 line required for .exe
        mov     ax,0    ; 2 clear ax by xor  ax,ax
        push    ax      ; 3 send ax to stack
        mov     ax,@data
        mov     ds,ax
        mov     al,n
        love
        love
        love
        love
        love
        love
pmain   endp
        end     pmain

6. โปรแกรมพิมพ์ 1 ถึง 15 โดยใช้ 2 macro
- macro ชื่อ jack ใช้พิมพ์ 1 ถึง 9 โดยแยกตัวเลขละ 1 บรรทัด
- macro ชื่อ jack ใช้พิมพ์ 10 ถึง 15 โดยใช้การแสดง 1 และ 0 ถึง 5 แยกกันคนละตัวอักษร
- ผู้เรียนต้องเข้าใจว่า 30h คือเลข 0

jack  macro
      mov    al,cl
      mov    bl,01h
      add    al,bl
      mov    sum,al
      mov    dl,sum
      mov    ah,2
      int    21h
      mov    cl,al
      lea    dx,lf
      mov    ah,9
      int    21h
      endm
mary  macro
      mov    dl,31h
      mov    ah,2
      int    21h
      mov    dl,bl
      mov    ah,2
      int    21
      mov    al,cl
      mov    bl,01h
      add    al,bl
      mov    sum,al
      mov    dl,sum
      mov    ah,2
      int    21h
      mov    cl,al
      lea    dx,lf
      mov    ah,9
      int    21h
      endm
; =============
; Main program
; =============
      .model small
      .data
n1    db     30h
n2    db     2fh
last1 db     39h
last2 db     35h
lf    db     0dh,0ah,'$'
sum   db     ?
      .code
pmain proc   far     ; can not change far to near 
      push   ds      ; 1 of 5 line required for .exe 
      mov    ax,0    ; 2 clear ax by xor  ax,ax
      push   ax      ; 3 send ax to stack
      mov    ax,@data
      mov    ds,ax
      mov    cl,n1
boy:  jack
      cmp    cl,last1
      jl     boy
      mov    cl,n2
girl: mary
      cmp    cl,last2
      jl     girl
      ret
pmain endp
      .stack 10h
      end    pmain

7. รับตัวอักษร และเลือกด้วย CMP ไปทำ Macro
- ถ้ารับตัวอักษร a ก็จะแสดงตัวอักษร C
- ถ้ารับตัวอักษรที่ไม่ใช่ a ก็จะไม่แสดงอะไรเลย
burin1	macro
	mov     ah,2
        mov     dl,43h ; C
        int     21h
	endm
	.model  small
        .data
        .code
pmain   proc    far      
        push    ds     
        mov     ax,0   
        push    ax     
        mov     ax,@data
        mov     ds,ax
        mov     ah,8
        int     21h
        cmp     al,61h ; a
	je      work1
        jmp     bye
work1:  burin1
bye:    ret
pmain   endp
        .stack  200h   
        end     pmain

8. แปลงโปรแกรมเข้ารหัส และถอดรหัสส่วนหนึ่งของ Kailas Jagtap เป็น macro
- โปรแกรมนี้นำมาจาก http://www.thaiall.com/assembly/loopbasic.htm
- มีต้นฉบับที่ http://www.laynetworks.com/Assembly%20Program7.htm โดย Kailas Jagtap
- โปรแกรมนี้แบ่งเป็น 4 procedure และ 4 macro
- ลบ Comment ออกเพื่อให้ดูสั้นลง
- ปรับไปอยู่ใน word ที่ kailas.docx
- http://www.thaiall.com/assembly/interrupt.htm
- http://www.emu8086.com/assembler_tutorial/8086_bios_and_dos_interrupts.html

; ตัวอย่างการย้ายบางส่วนของโปรแกรมมาไว้ใน macro   
; โปรแกรมนี้ ได้แยกการทำงาน readnext ที่ทำงานเหมือนกันแต่เขียนต่างกันไว้ คือ encrypt เขียน readnext ใน label
; แต่ decrypt จะเขียน readnext ออกมาเป็นอีก macro หนึ่ง เป็นการทำงานร่วมกันของ burin3 และ burin4
; เขียนบล็อก http://www.thaiall.com/blog/burin/3691/
burin4 macro ; move readnext1 in macro
      mov ah,3fh  ; อ่านข้อมูลที่ handle ไว้ เข้ามาในหน่วยความจำ 512 Bytes
      mov bx,handle
      mov cx,512     
      lea dx,buffer  
      int 21h

      jc err_out1 ; ถ้าอ่านได้ จะทำให้ carry เป็น 0

      mov save_ax,ax  ; ค่าของ ax คือจำนวนไบท์ที่อ่านได้
      cmp ax,0     
      je done4  ; ถ้ากระโดดไป แสดงว่าจบแฟ้ม เพราะขนาดของ ax = 0     	  
      call decorrupt 

      mov ah,40h     ; เขียนข้อมูลขนาดเท่า save_ax ลงไปใน newhandle    
      mov bx,newhandle
      mov cx,save_ax
      lea dx,buffer  ; ข้อมูลที่ผ่านการยกเลิกแผลงหรือไขว้ (decorrupt) มาแล้ว  
      int 21h

      jc err_out1    ; ถ้าเขียนได้ จะทำให้ carry เป็น 0    
      jmp readnext1  ; กระโดดไปเริ่มอ่านข้อมูลต่อจากจุดเดิม  
	endm

burin3 macro ; move decr in macro
      call disp_nl   
      mov ah,9 ; แสดงข้อความว่า รับชื่อแฟ้มที่ต้องการ DECRYPT   
      lea dx,s3
      int 21h

      lea dx,fname   ; กำหนดชื่อแฟ้มอ้างอิง เมื่อรับผ่านแป้นพิมพ์
      call getname   

      mov ah,3dh  ; เปิดแฟ้มข้อมูล
      lea dx,fname+2
      mov al,0
      int 21h

      mov handle,ax  ; เก็บตำแหน่งอ้างอิงแฟ้ม
      jc err_out  ; ถ้าเปิดได้ จะทำให้ carry เป็น 0

      mov ah,9     ; แสดงข้อความว่า รับชื่อแฟ้มที่ต้องการ DECRYPT     
      lea dx,s33
      int 21h

      lea dx,newf1  ; กำหนดชื่อแฟ้มอ้างอิง เมื่อรับผ่านแป้นพิมพ์ 
      call getname  
	  
      mov ah,3ch ;  สร้างไฟล์ใหม่    
      lea dx,newf1+2
      mov cx,0
      int 21h

      mov newhandle,ax ; เก็บตำแหน่งอ้างอิงแฟ้ม 
      jc err_out  ; ถ้าสร้างได้ จะทำให้ carry เป็น 0  
	endm

burin2 macro ; move encr in macro
      call disp_nl   

      mov ah,9   ; แสดงข้อความว่า รับชื่อแฟ้มที่ต้องการ ENCRYPT  
      lea dx,s2
      int 21h

      lea dx,fname ; กำหนดชื่อแฟ้มอ้างอิง เมื่อรับผ่านแป้นพิมพ์
      call getname   

      mov ah,3dh ; เปิดแฟ้มข้อมูล    
      lea dx,fname+2 
      mov al,0     
      int 21h      

      mov handle,ax  ; เก็บตำแหน่งอ้างอิงแฟ้ม
      jc err_out2  ; ถ้าเปิดได้ จะทำให้ carry เป็น 0

      mov ah,9  ; แสดงข้อความว่า รับชื่อแฟ้มที่ต้องการ ENCRYPT    
      lea dx,s22 
      int 21h

      lea dx,newf1 ; กำหนดชื่อแฟ้มอ้างอิง เมื่อรับผ่านแป้นพิมพ์
      call getname   

      mov ah,3ch ; สร้างไฟล์ใหม่    
      lea dx,newf1+2 
      mov cx,0     
      int 21h      

      mov newhandle,ax ; เก็บตำแหน่งอ้างอิงแฟ้ม 
      jc err_out  ; ถ้าสร้างได้ จะทำให้ carry เป็น 0  

readnext:
      mov ah,3fh  ; อ่านข้อมูลที่ handle ไว้ เข้ามาในหน่วยความจำ 512 Bytes   
      mov bx,handle  
      mov cx,512     
      lea dx,buffer 
      int 21h      

      jc err_out  ; ถ้าอ่านได้ จะทำให้ carry เป็น 0

      mov save_ax,ax ; ค่าของ ax คือจำนวนไบท์ที่อ่านได้
      cmp ax,0     
      je done1 ; ถ้ากระโดดไป แสดงว่าจบแฟ้ม เพราะขนาดของ ax = 0     

      call corrupt   

      mov ah,40h ; เขียนข้อมูลขนาดเท่า save_ax ลงไปใน newhandle    
      mov bx,newhandle      
      mov cx,save_ax 
      lea dx,buffer ; ข้อมูลที่ผ่านการ แผลงหรือไขว้ (corrupt) มาแล้ว  
	  int 21h      

      jc err_out ; ถ้าเขียนได้ จะทำให้ carry เป็น 0    
      jmp readnext ; กระโดดไปเริ่มอ่านข้อมูลต่อจากจุดเดิม  
      err_out2: jmp err_out
     endm

burin1 macro ; move rpt_disp in macro
      mov ah,9  ; แสดงคำว่า E หรือ D
      lea dx,s1 
      int 21h

      mov ah,1  ; รับตัวอักษร 1 ตัว   
      int 21h

      cmp al,'E'     
      je encr  ; กระโดดไปยัง encr (ENCRYPT)
      cmp al,'e'
      je encr

      cmp al,'D'     
      je decr2 ; กระโดดไปยัง decr2 (DECRYPT)
      cmp al,'d'
      je decr2

      cmp al,'X'     
      je stop2
      cmp al,'x'
      je stop2

      mov ah,9  ; แสดงข้อความว่าตัวเลือกที่ส่งมา ไม่ถูกต้อง   
      lea dx,s4 
      int 21h

      jmp rpt_disp   
     endm

	.model  small
      .data      
s0      db      0dh,0ah,'PROGRAM FOR FILE ENCRYPTION AND DECRYPTION ..By Kailas Jagtap$'
s1      db      0dh,0ah,'Do you want to ENCRYPT(E) or DECRYPT(D) a file ? : $'
s2      db      0dh,0ah,'Enter Name of File(to be encrypted): $'
s22     db      0dh,0ah,'Enter Name of File(which will store encrypted data): $'
s3      db      0dh,0ah,'Enter Name of encrypted File(to be decrypted): $'
s33     db      0dh,0ah,'Enter Name of File(which will store decrypted data): $'
s4      db      0dh,0ah,'***************** ERROR!!! - INVALID INPUT ****************** $'
s44     db      0dh,0ah,'***************** ERROR IN FILE OPERATION !!!!!! ************ $'
s5      db      0dh,0ah,'$'
s6      db      0dh,0ah,'.............. File ENCRYPTED Successfully ! ................. $'
s7      db      0dh,0ah,'.............. File DECRYPTED Successfully ! ................. $'
fname   db      80     
        db      0      
        db      80 dup(0)      
newf1   db      80     
        db      0      
        db      80 dup(0)     
save_ax dw      1 dup(0)      
buffer  db      512 dup(0)      
endbuffer db    '$'    
handle  dw      ?      
newhandle dw    ?      

      .code
start: 
      mov ax,@data   
      mov ds,ax

      call disp_nl   
      mov ah,9     
      lea dx,s0 
      int 21h

rpt_disp: burin1
decr2:  jmp decr1      
stop2:  jmp stop1      
encr:   burin2      
decr1:  jmp decr     
stop1:  jmp stop     
done1:  lea dx,s6   ; พิมพ์ว่า การเข้ารหัส สำเร็จลุล่วงไปด้วยดี   
      jmp done3
done2:  lea dx,s7   ; พิมพ์ว่า การถอดรหัส สำเร็จลุล่วงไปด้วยดี      
done3:  mov ah,9 
      int 21h      
      jmp done
stop:   mov ah,4ch     
      int 21h


getname proc near
      mov ah,0ah   ; รับข้อมูล String จากแป้มพิมพ์  
      int 21h      

      mov si,dx  ; บันทึกตำแหน่งของชื่อแฟ้มที่รับจากแป้นพิมพ์    
      mov bl,[si+1]  
      add si,02      
      sub bh,bh      
      mov BYTE PTR[si+bx],0     

      ret      
getname endp
     
err_out: 
      mov ah,9    
      lea dx,s44 ; การดำเนินการกับแฟ้ม ผิดพลาด
      int 21h
      jmp stop     

done: 
      mov ah,3eh  ; ปิดแฟ้มให้เรียบร้อย ก่อนจบการทำงาน
      mov bx,handle  
      int 21h      
      mov bx,newhandle      
      int 21h      
      jmp stop     

done4:  
      jmp done2      
  
corrupt proc near      
      lea si,buffer ; ส่งตำแหน่ง buffer ให้ si (Load effective adress)
      mov cx,save_ax ; ค่า cx คู่กับ loop บอกว่า จะให้ทำใน loop กี่รอบ
again: 
      mov al,[si] ; สำเนาค่าของ si ที่ตำแหน่งปัจจุบัน ให้กับ al     
      add al,07h ; บวกอีก 7 ให้กับ al เช่นเดิมคือ a ก็จะเป็น  h  
      mov [si],al ; ส่งผลการ corrupt ไปแทนค่าในตำแหน่งเดิม   
      inc si ; ตรวจ    
      loop again     
      ret
corrupt endp
  
err_out1: jmp err_out
decr:   burin3
readnext1: burin4

decorrupt proc near    
      lea si,buffer ; ส่งตำแหน่ง buffer ให้ si (Load effective adress) 
      mov cx,512 ; ค่า cx คู่กับ loop บอกว่า จะให้ทำใน loop กี่รอบ
again1: 
      mov al,[si] ; สำเนาค่าของ si ที่ตำแหน่งปัจจุบัน ให้กับ al     
      sub al,07h ; ลบอีก 7 ให้กับ al เช่นเดิมคือ 8 ก็จะเป็น  1  
      mov [si],al ; ส่งผลการ corrupt ไปแทนค่าในตำแหน่งเดิม  
      inc si
      loop again1
      ret
decorrupt endp

disp_nl proc near      
      mov ah,9     
      lea dx,s5      
      int 21h      
      ret
disp_nl endp


      .stack  200h
      end      start