Tin học cơ bản

Học tập, chia sẻ kiến thức cơ bản về tin học.

Sử dụng Interrupt trong chương trình assembly (Phần 2)

MỘT SỐ VÍ DỤ

Ví dụ 1: Giả sử hàm X của ngắt Y khi được gọi sẽ trả về trong cặp thanh ghi ES:SI địa chỉ của vùng nhớ chứa tên của nhà sản xuất (nhãn hiệu) của vi xử lý đang sử dụng trên máy tính hiện tại, tên này dài không quá 8 kí tự.

Các lệnh sau đây sẽ in tên nhà sản xuất nói trên ra màn hình:

                    Mov        Ah, X                             ; hàm cần gọi được đưa vào thanh ghi ah

                    Int           Y                                  ; gọi ngắt Y với hàm X

                    ;--------------------      

                    Mov        Cx, 8                              ; tên dài không quá 8 kí tự

                    Mov        Ah, 02                            ; dùng hàm 02/21h để in kí tự ra nàm hình

             LapIn:

                    Mov        Dl, Byte PTR ES:[SI]

                    Int           21h

                    INC         SI                                 ; đến kí tự tiếp theo

                    Loop       LapIn

                    ;------------------------------------

Ví dụ 2: Giả sử hàm X của ngắt Y khi được gọi với AL = 1 (được gọi là hàm con) sẽ trả về trong cặp thanh ghi ES:DI địa chỉ của vùng nhớ chứa ngày-tháng-năm sản xuất ROM-BIOS đang sử dụng trên máy tính hiện tại. Đồng thời hàm/ngắt này cũng cho biết số kí tự trong xâu ngày-tháng-năm trong thanh ghi BX.

Các lệnh sau đây sẽ in xâu ngày-tháng-năm nói trên ra màn hình:

                    Mov        Ah, X                            ; hàm cần gọi được đưa vào thanh ghi ah

                    Mov        Al, 1                             ; gọi hàm X với Al = 1

                    Int           Y                                 ; gọi ngắt Y với hàm X

                    ;--------------------

                    Mov        Cx, Bx                          ; đưa số kí tự của xâu ngày-tháng-nămvào Cx

                    Mov        Ah, 02                          ; dùng hàm 02/21h để in kí tự ra nàm hình

             LapIn:

                    Mov        Dl, Byte PTR ES:[DI]

                    Int           21h

                    INC         DI                                ; đến kí tự tiếp theo

                    Loop       LapIn

                    ;-------------------------------------

Ví dụ 3: Hàm 39h của ngắt 21h được sử dụng để tạo thư mục con trên đĩa. Hàm này quy định: DS:DX chứa xâu tên thư mục cần tạo, bao gồm cả đường dẫntìm đến thư mục này, xâu này kết thúc bởi trị 0. Nếu việc tạo không thành công thì Cf = 1, khi đó thanh ghi Ax sẽ chứa mã lỗi.

Các lệnh sau đây sẽ tạo ra thư mục con BTCB trong thư mục ASSEM trên thư mục gốc ổ đĩa D. Chương trình phải khai báo biến TenTM, chứa xâu tên thư mục cần tạo như sau:

            TenTM           DB      ‘D:\ASSEM\BTCB’,0

Các lệnh:

            Mov    Ax, Seg TenTM

            Mov    DS, Ax

            Mov    Dx, Offset TenTM

            Mov    Ah, 39h

            Int       21h

            ;-------------------------

            Jc        TB_Loi                                   ; nếu CF = 1 thì việc tạo bị lỗi

            <In ra thong bao hoan thanh>

            Jmp     KetThuc

       TB_Loi:

            <In ra thong bao khong tao duoc thu muc>

        KetThuc:

            ...

            ;----------------------------------------------------  

Ví dụ 4: Sau đây là chương trình dạng COM: In ra tất cả (256) các kí tự có trong bảng mã ASCII:

.Model       Small

.Code

      ORG       100h

          Start:

                    Jmp         Main

    TB       DB    'Noi dung Bang ma ASCII:',0Ah,0Dh,'$'

      Main           Proc

                    Mov        Ah, 09h

                    Lea          Dx, TB

                    Int           21h

                    ;--------------------------

                    Mov        Cx, 256

                    Mov        Ah, 02

                    Mov        Dl, 0

            LapIn:

                    Int           21h

                    ;-----------------

                    Mov        Bl, Dl

                    Mov        Dl, ' '

                    Int           21h

                    Mov        Dl, Bl

                    ;-----------------

                    INC         Dl

                    Loop       LapIn

                    ;-------------------------

                    Int           20h

    Main        Endp

                    End         Start



Ví dụ 5.1: Sau đây là chương trình dạng COM: Nhập vào một xâu kí tự bất kỳ. Sau đó in ra lại chính xâu kí tự vừa được nhập vào nhập. Sử dụng hàm 09/21h để in ra.

.Model       Small

.Code

                    ORG       100h

    Start:

                        JMP    Main

              TBN                DB    'Nhap vao mot xau ki tu: $'

              TBX                DB    0Ah,0Dh,'Xau vua nhap: $'

              Buff                DB    100,0,100 Dup (' ')

   Main           Proc

                    Mov        Ah,  09h

                    Lea          Dx, TBN

                    Int           21h

                    ;--------------------------

                    Mov        Ah, 0Ah

                    Lea          Dx, Buff

                    Int           21h

                    ;-------------------------

                    Mov        Cx, 0

                    Mov        Cl,  Buff[1]

                    Add         Dx, 2

                    Mov        DI, Dx

                    Add         DI, Cx

                    Mov        Byte PTR [DI], ‘$’

                    ;-------------------------

                    Mov        Ah,  09h

                    Lea          Dx, TBX

                    Int           21h

                    ;-------------------------

                    Mov        Ah,  09h

                    Lea          Dx, Buff

                    Add         Dx, 2

                    Int           21h

                    ;-------------------------

                    Int           20h

     Main           Endp

                    End         Start



Ví dụ 5.2: Sau đây là chương trình dạng EXE: Nhập vào một xâu kí tự bất kỳ. Sau đó in ra lại chính xâu kí tự vừa được nhập vào nhập. Sử dụng hàm 02/21h để in ra.

.Model       Small

.Stack        100h

.Data

              TBN                DB    'Nhap vao mot xau ki tu: $'

              TBX                DB    0Ah,0Dh,'Xau vua nhap: $'

              Buff                DB    100,0,100 Dup (' ')

.Code

          Main           Proc

                    Mov        Ax,@Data

                    Mov        DS, Ax

                    ;-------------------------

                    Mov        Ah,  09h

                    Lea          Dx, TBN

                    Int           21h

                    ;--------------------------

                    Mov        Ah, 0Ah

                    Lea          Dx, Buff

                    Int           21h

                    ;-------------------------

                    Mov        Cx, 0

                    Mov        Cl,  Buff[1]

                    Add         Dx, 2

                    Mov        DI, Dx

                    ;--------------------------

                    Mov        Ah,  09h

                    Lea          Dx, TBX

                    Int           21h

                    ;-------------------------

                    Mov        Ah, 2

       Lap_In:                        

                    Mov        Dl, [DI]

                    Int           21h

                    INC         DI

                    Loop       Lap_In

                    ;-------------------------

                    Mov        Ah, 4Ch

                    Int           21h

   Main           Endp

            End  

Ví dụ 6: Sau đây là chương trình dạng COM: Nhập vào một kí tự thường, chương trình sẽ in ra kí tự in hoa tương ứng.



.Model       Small

.Code

            ORG   100h

Start:

            Jmp     Main

              TB1                 DB    'Nhap vao mot ki tu thuong: $'    

              TB2                 DB    0Ah,0Dh,'Ki tu hoa tuong ung: $'



 Main           Proc

                    Mov        Ah, 09h

                    Lea          Dx, TB1

                    Int           21h

                    ;----------------------

                    Mov        Ah, 01

                    Int           21h

                    Mov        Bl, Al

                    ;-----------------------

                    Mov        Ah, 09h

                    Lea          Dx, TB2

                    Int           21h

                    ;----------------------

                    Mov        Ah, 02

                    Mov        Dl, Bl

                    Sub         Dl, 20h

                    Int           21h

                    ;-------------------------

                    Int           20h

 Main           Endp

         End        Start

Ví dụ 7: Sau đây là chương trình dạng COM: Nhập vào một xâu kí tự sau đó in ra lại xâu đã nhập theo thứ tự đảo ngược.

.Model       Small

.Code

            ORG   100h

Start:

            JMP    Main

              TBN                DB    'Nhap vao mot xau ki tu: $'

              TBX                DB    0Ah,0Dh,'Xau vua nhap: $'

              Buff                DB    100,0,100 Dup (' ')

 Main           Proc

                    Mov        Ah,  09h

                    Lea          Dx, TBN

                    Int           21h

                    ;--------------------------

                    Mov        Ah, 0Ah

                    Lea          Dx, Buff

                    Int           21h

                    ;-------------------------

                    Mov        Cx, 0

                    Mov        Cl,  Buff[1]

                    ;---------------------------

                    Mov        Ah,  09h

                    Lea          Dx, TBX

                    Int           21h

                    ;-------------------------

                    Lea          Dx, Buff

                    Add         Dx, 2

                    Mov        DI, Dx

                    Add         DI, Cx

                    Dec         DI

                    Mov        Ah, 02

        Lap_In_Nguoc:

                    Mov        Dl, [DI]

                    Int           21h

                    DEC        DI

                    Loop       Lap_In_Nguoc

                    ;--------------------------------

                    Int           20h

 Main           Endp

            End     Start



Ví dụ 8:,Sau đây là chương trình dạng COM: Nhập vào hai số (số thứ nhất: nhỏ hơn 5; số thứ hai: nhỏ hơn hoặc bằng 5), sau đó in ra tổng của hai số vừa nhập.

.Model       Small

.Code

        ORG       100h

Start:

        Jmp         Main

              TBN1              DB        'Nhap so hang thu nhat (nho hon 5): $'

              TBN2              DB        0Ah,0Dh,'Nhap so hang thu hai (nho hon bang 5): $'

              TBX                DB        0Ah,0Dh,'Tong cua hai so la: $'

 Main           Proc

        Mov        Ah, 09h

                    Lea          Dx, TBN1

        Int           21h

        Mov        Ah, 01

        Int           21h

        Mov        Bl, Al

        Sub         Bl, 30h

        ;-------------------------

        Mov        Ah, 09h

        Lea          Dx, TBN2

        Int           21h

        Mov        Ah, 01

        Int           21h

        Sub         Al, 30h

        Add         Bl, Al

        ;-------------------------

        Mov        Ah, 09h

        Lea          Dx, TBX

        Int           21h                          

        Mov        Ah, 02

        Mov        Dl, Bl

        Add         Dl, 30h

        Int           21h

        ;------------------------

        Int           20

Main           Endp

         End              Start        

Sử dụng Interrupt trong chương trình assembly (Phần 1)

NGẮT TRONG CHƯƠNG TRÌNH ASSEMLBLY

Ngắt (Interrupt) là các tín hiệu mà các thành phần trong hệ thống, như: thiết bị ngoại vi, hệ điều hành, chương trình người sử dụng, ..., gửi đến vi xử lý (họ Intel) mỗi khi nó cần trao đổi thông tin với vi xử lý hay cần được sự phục vụ từ vi xử lý. Ngắt cũng có thể phát sinh từ chính bên trong vi xử lý khi nó phát hiện một lỗi nghiêm trong xảy ra trong quá trình xử lý của nó. Khi nhận được một tín hiệu yêu cầu ngắt vi xử lý sẽ dừng ngay thao tác (lệnh) hiện tại để xem xét và đáp ứng yêu cầu ngắt đó, sau đo mới tiếp tục lại từ thao tác (lệnh) mà nó bị dừng trước đó.        

Các máy IBM_PC, sử dụng vi xử lý Intel, bao gồm nhiều loại ngắt: Ngắt phần cứng, ngắt phần mềm, ngắt bên trong, ngắt không chắn được (ngắt có độ ưu tiên cao nhất). Trong đó ngắt phần mềm là các ngắt được phát sinh từ hệ điều hành và chương trình của người sử dụng, điều này thường thấy trong các chương trình hợp ngữ.

Hệ thống các ngắt mềm bên trong máy tính IBM_PC được cung cấp bởi BIOS và hệ điều hành, gồm 256 ngắt, chúng được đánh số từ 0 đến 255 và được gọi là số hiêu ngắt. Mỗi ngắt mềm tương ứng với một chương con được gọi là chương trình con phục vụ ngắt, tức là, để đáp ứng một yêu cầu ngắt mềm thì hệ thống sẽ phải thực hiện một chương trình con phục vụ ngắt tương ứng.

Hợp ngữ cung cấp lệnh Int để các chương trình gọi một ngắt mềm khi cần. Khi gọi ngắt mềm chương trình phải chỉ ra số hiệu ngắt cần gọi, khi đó hệ thống sẽ phải gọi thực hiện chương trình con phục vụ ngắt tương ứng để đáp ứng yêu cầu gọi này.

 Trong môi trường hệ điều hành 16 bít MSDOS, ngay sau khi khởi động, hệ điều hành nạp tất cả 256 chương trình con phục vụ ngắt vào bộ nhớ. Khi đó 256 địa chỉ (Segment:Offset) các vùng nhớ, nơi  định vị của 256 chương trình con phục vụ ngắt, sẽ được lưu trữ tại một không gian nhớ đặc biệt trong bộ nhớ, vùng nhớ này được gọi là bảng vector ngắt (mỗi địa chỉ định vị của một chương trình con phục vụ ngắt được gọi là một vector ngắt.

Như vậy khi hệ thống nhận được một yêu cầu ngắt n (Int   n) từ chương trình thì nó phải: 1. Dừng lệnh hiện tại, đưa giá trị con trỏ lệnh CS:IP hiện tại (đó chính là địa chỉ của lệnh sau lệnh gọi ngắt) vào lưu trữ ở hai phần tử trên đỉnh Stack; 2. Xác định phần tử (địa chỉ) trong bảng vector ngắt chứa vector ngắt của ngắt n, lấy giá trị tại đây (2 phần tử liên tiếp) để đưa vào cặp thanh ghi con trỏ lệnh CS:IP (đây chính là địa chỉ của lệnh đầu tiên của chương trình con phục vụ ngắt n trong bộ nhớ); 3. Bắt đầu thực hiện chương trình con phục vụ ngắt cho đến khi gặp lệnh kết thúc chương trình này, đó là lệnh Iret; 4. Lấy nội dung của hai phần tử trên đỉnh Stack đặt vào lại cặp thanh ghi con trỏ lệnh để quay trở lại tiếp tục thực hiện lệnh sau lệnh gọi ngắt trong chương trình.

Những vấn đề liên quan đến Stack và cơ chế của một lời gọi ngắt sẽ được chúng tôi làm rõ hơn ở phần sau của tài liệu này.  

Lệnh Int

Cú pháp:


Trong đó: Trong đó <n> là số hiệu ngắt của một ngắt mềm cần gọi. Tức là, n có thể là một trong các giá trị từ 0 đến 255, đó chính là số hiệu ngắt của 256 ngắt mềm mà BIOS và MSDOS cung cấp.

Tác dụng: Lệnh Int (Interrupt) được sử dụng để gọi một ngắt mềm (của BIOS hoặc MSDOS) trong chương trình hợp ngữ. Khi một ngắt mềm được gọi thì hệ thống sẽ thực hiện chương trình con phục vụ ngắt tương ứng với nó.

Ví dụ 1:

                       Int           10h                 ; gọi ngắt 10h của BIOS

                       Int           20h                 ; gọi ngắt 20h của MSDOS

Một ngắt mềm, hay chính xác hơn là một chương trình con phục vụ ngắt, có thể thực hiện nhiều chức năng khác nhau, mỗi chức năng này được thể hiện thông qua một con số, được gọi là số hiệu hàm của ngắt. Do đó, trước khi gọi ngắt chương trình phải chỉ rõ gọi ngắt với hàm nào, bằng cách đặt số hiệu hàm cần gọi vào thanh ghi AH.

Ví dụ 2:

        Mov        Ah, 01                        ; gọi ngắt 21h với hàm 01, Hay nói ngược lại: gọi hàm

        Int           21h                 ; 01 của ngắt 21h

Trước khi gọi hàm/ngắt chương trình cần cung cấp đầy đủ dữ liệu vào (nếu có) cho nó, sau khi hàm/ngắt được thực hiện chương trình cần xác định rõ nó có trả về dữ liệu kết quả (dữ liệu ra) hay không, nếu có thì chứa ở đâu: thanh ghi hay ô nhớ, và có tác động đến các cờ hiệu hay không.



Ví dụ 3:

        Mov        Ah, 02                                    ; đặt số hiệu hàm cần gọi vào AH

        Mov        DH, 10                        ; cung cấp dữ liệu vào thứ nhất vào DH

        Mov        DL, 20                        ; cung cấp dữ liệu vào thứ hai vào DL

        Int           10h                             ; gọi ngắt 10h với hàm 02. Hàm/ngắt này không

                                                            ; trả về dữ liệu kết quả.

Dãy lệnh trên thực hiện việc gọi hàm 02 của ngắt 10h (ngắt của BIOS), nó thực hiện việc dịch chuyển con trỏ đến dòng 10 cột 20 của màn hình văn bản.

Ví dụ 4:

        Mov        Ah, 2Bh                      ; gọi ngắt 21h với

        Int           21h                             ; hàm 2Bh

Hàm này trả về ngày-tháng-năm hiện tại (theo đồng hồ hệ thống trên máy tính). Cụ thể: Thanh ghi CX (1980-2099) chứa năm hiện tại, thanh ghi DH (1-12) chứa tháng hiện tại, thanh ghi DL (1-31) chứa ngày hiện tại. Đồng thời AL cho biết ngày trong tuần (0 : chủ nhật, 6 : thứ 7).

Một số hàm của ngắt 21h (MSDOS)

Ngắt 21h của MSDOS là ngắt thường được sử dụng nhất, nên ở đây chúng tôi chọn giới thiệu về  ngắt này, nhưng chỉ với các hàm vào/ra kí tự/xâu kí tự cơ bản. Chức năng của mỗi  ngắt, chức năng của các hàm trong một ngắt, các yêu cầu dữ liệu vào/ra của từng hàm trong mỗi ngắt,... dễ dàng tìm thấy trong các tài liệu về lập trình hệ thống.

Hàm 02 của ngắt 21h:

Tác dụng: In một kí tự ra màn hình. Kí tự (hoặc mã ASCII của kí tự) cần in được đặt trước trong thanh ghi DL. Kí tự ở đây có thể là kí tự thường hoặc kí tự điều khiển.

Sử dụng:

Vào:   Ah = 02

            Dl = <Kí tự cần in ra>

Ra:     Không có

Ví dụ 1: Các lệnh sau đây in ra màn hình kí tự A:      

            Mov          Ah, 02

            Mov          Dl, ‘A’                  ;có thể viết lệnh  Mov   Dl, 41h      

            Int             21h                       ; 41h là mã ASCII của kí tự A

Ví dụ 2: Các lệnh sau đây in ra màn hình 10 kí tự, bắt đầu từ kí tự A:

            Mov          Cx, 10

            Mov          Ah, 02

            Mov          Dl, ‘A’

     Lap_In:

            Int             21h

            INC           DL

            Loop         Lap_In

Ví dụ 3: Các lệnh sau đây in xâu kí tự từ trong biến TieuDe ra màn hình. Giả sử rằng biến TieuDe đã được khai báo như sau:

            TieuDe     DB            ‘Khoa CNTT Truong DHKH Hue’

Các lệnh:

            Lea            DI, TieuDe

            Mov          Cx, 25

            Mov          Ah, 02

     Lap_In:

            Mov          Dl, Byte PTR [DI]

            Int             21h

            INC           DI

            Loop         Lap_In

            ;---------------------------

Ví dụ 4: Giả sử tại địa chỉ 0A00:0100 trong bộ nhớ có chứa một xâu kí tự ASCII, gồm 50 kí tự. Các lệnh sau đây sẽ in xâu kí tự nói trên ra màn hình.

            Mov          Ax, 0A00h

            Mov          ES, Ax

            Mov          DI, 0100h

            ;-------------------------

            Mov          Cx, 50

            Mov          Ah, 02

      Lap_In:

            Mov          Dl, Byte PTR ES:[DI]

            Int             21h

            INC           DI

            Loop         Lap_In

            ;--------------------------------------

Hàm 09 của ngắt 21h:

Tác dụng: In một dãy kí tự (một xâu kí tự) ra màn hình. Địa chỉ của xâu cần in này phải được chỉ bởi cặp thanh ghi DS:DX và xâu phải được kết thúc bởi dấu $.

Sử dụng:

Vào:   Ah = 09

            DS:DX = <Segment:Offset của xâu cần in ra>

Ra:     Không có

Ví dụ 1: Giả sử chương trình đã khai báo biến TieuDe. Viết lệnh in nội dung của biến TieuDe ra màn hình:

-     Trong trường hợp này biến TieuDe phải được khai báo trước như sau:

                     TieuDe     DB   ‘Truong DH Khoa hoc Hue$’

-     Và đoạn lệnh gồm các lệnh sau:

            Mov          Ah, 09

            Mov          Ax, Seg TieuDe

            Mov          DS, Ax

            Mov          Dx, Offset TieuDe          ; có thể dùng lệnh Lea      TieuDe

            Int             21h

Trong thự tế, với các chương trình hợp ngữ viết ở dạng đoạn giản đơn, thì không cần đưa địa chỉ Segment của biến cần in vào DS. Bởi vì:

-     Đối với các chương trình dạng COM:

-     Đối với các chương trình dạng EXE:

Ví dụ 2: Giả sử biến TieuDe đã được khai báo như sau:

            TieuDe     DB            ‘Khoa CNTT Truong DHKH Hue$’

Các lệnh sau chỉ in các kí tự “Truong DHKH Hue” từ biến TieuDe ra màn hình:

            Mov          Ax, Segment TieuDe

            Mov          DS, Ax

            Mov          Dx, TieuDe

            Add           Dx, 11

            Mov          Ah, 09

            Int             21h        

Các lệnh sau chỉ in các kí tự “Khoa CNTT” từ biến TieuDe ra màn hình:

            Mov          Ax, Segment TieuDe

            Mov          DS, Ax

            Mov          Dx, TieuDe

            Mov          DI, Dx

            Add           DI, 10

            Mov          Byte PTR DS:[DI], ‘$’

            Mov          Ah, 09

            Int             21h        

Ví dụ 3: Giả sử tại địa chỉ 0A00:0100 trong bộ nhớ có chứa một xâu kí tự ASCII, gồm 50 kí tự. Các lệnh sau đây sẽ in xâu kí tự nói trên ra màn hình.

            Mov          Ax, 0A00h

            Mov          DS, Ax

            Mov          Dx, 0100h

            ;-------------------------

            Mov          DI, Dx

            Mov          Cx, 50

            Add           DI, Cx

            Mov          Byte PTR DS:DX, ‘$’

            ;-------------------------------------

            Mov          Ah, 09

            Int             21h

            ;-------------------

Hàm 01 của ngắt 21h:

Tác dụng:Nhập một kí tự từ bàn phím vào lưu trong thanh ghi AL. Cụ thể là, AL sẽ chứa mã ASCII của kí tự ghi trên phím nhập.

Sử dụng:

Vào:   Ah = 01

Ra:     Al = 0: nếu phím nhập là một trong các phím chức năng

            Al = < mã ASCII của kí tự ghi trên phím nhập>

Cụ thể như sau: Khi chương trình gọi ngắt 21h với hàm 01 thì màn hình sẽ xuất hiện một con trỏ nhập, để người sử dụng nhập vào một kí tự từ bàn phím. Khi đó, nếu người sử dụng gõ một trong các phím chức năng thì AL nhận được giá trị 0, nếu người sử dụng gõ một phím kí tự nào đó thì AL nhận được mã ASCII của kí tự đó.

Chú ý: Hàm 08 của ngắt 21h có chức năng tương tự hàm 01 ngắt 21h nhưng kí tự trên phím gõ không xuất hiện trên màn hình, tất cả đều được xuất hiện bởi kí tự ‘*’.

Ví dụ 1:

            Mov          Ah, 01                  ; với hai lệnh này màn hình sẽ xuất hiện con trỏ

            Int             21h                       ; nhập để người sử dụng nhập một kí tự vào AL

Ví dụ 2: Các lệnh sau đây cho phép nhập một xâu kí tự, với số lượng kí tự được ấn định trước, vào biến LuuXau đã khai báo trước trong chương trình

Giả sử biến LuuXau được khai báo như sau:

            LuuXau         DB      30 Dup (‘ ‘)

Các lệnh:

            Mov          Ax, Seg LuuXau

            Mov          DS, Ax

            Lea            DI, LuuXau      

            ;--------------------------------

            Mov          Cx, 30

            Mov          Ah, 01

     Nhap_Xau:

            Int             21h

            Mov          Byte PTR DS:[DI], Al

            INC           DI

            Loop         Nhap_Xau

            ;--------------------------------------

Trong trường hợp này chúng ta đã giả sử: Người sử dụng chỉ nhập các kí tự (gõ phím kí tự để nhập), không nhập các phím chức năng.

Trong thực tế, không bao giờ chúng ta sử dụng hàm 01 ngắt 21h để nhập xâu, vì nó tồn tại hai hạn chế: không thể kết thúc nhập bằng cách gõ phím Enter; số kí tự của xâu nhập phải được ấn định trước trong chương trình. Để khắc phục, MSDOS cung cấp hàm 0Ah của ngắt 21h để hỗ trợ nhập xâu kí tự.

Hàm 0Ah của ngắt 21h:

Tác dụng:Nhập một xâu kí tự vào một biến đệm cho trước trong chương trình, biến này phải được chỉ bởi cặp thanh ghi DS:DX. Và biến đệm phải có dạng như sau:

-         Byte 0: chứa số kí tự tối đa của xâu nhập vào

-         Byte 1: chứa một trị không (= 0)

-         Byte 2, byte 3, byte 4, ... chứa một trị rỗng (để trống), để chứa các kí tự sẽ được nhập vào sau này (khi chương trình thực hiện).

Để có được một biến như trên chương trình phải khai báo biến (tên biến là Xau_Nhap) như sau:

            Xau_Nhap     DB      256, 0, 256 Dup (‘ ‘)

Như vậy Xau_Nhap là một biến kiểu byte, gồm 258 byte. Byte đầu tiên (byte) chứa trị 256, byte 1 chứa trị 0, 256 byte tiếp theo chứa kí tự trống, được sử dụng để chứa các kí tự sẽ được nhập sau này. Xau_Nhap chứa tối đa 256 kí tự.

Cũng có thể sử dụng hàm 0Ah/21h để nhập một xâu kí tự vào vùng nhớ có địa chỉ xác định trong bô nhớ.

Sử dụng:

Vào:  Ah = 0Ah    

            DS:DX = <Địa chỉ Segment:Offset của xâu nhập>

Ra:     DS:DX không thay đổi

Biến đệm bây giờ có dạng như sau:

-         Byte 0: không thay đổi

-         Byte 1: chứa tổng số kí tự đã được nhập vào

-         Byte 2, byte 3, byte 4, ... chứa các kí tự đã được nhập vào.

Ví dụ 1: Với khai báo biến đệm Xau_Nhap như trên, nếu sau này chương trình nhập vào xâu: “Tin hoc” thì:

-         Byte 0: vẫn chứa số 256

-         Byte 1: chứa giá trị 7, đó chính là 7 kí tự trong xâu “Tin hoc”

-         Từ byte 2 đến 8 chứa lần lượt các kí tự trong xâu “Tin hoc.

Ví dụ 2: Giả sử chương trình đã khai báo xâu TieuDe như sau:

TieuDe           DB      100, 0, 100 Dup (‘ ‘)

            Các lệnh sau đây sử dụng hàm 0Ah/21h để nhập một xâu kí tự vào biến TieuDe:

            Mov          Ax, Seg TieuDe

            Mov          Ds, Ax

            Lea            Dx, TieuDe

            Mov          Ah, 0Ah

            Int             21h

Các lệnh sau đây lấy số kí tự thực sự nhập đưa vào lưu trữ trong thanh ghi Cx:

            Mov          Cx, 0

            Mov          Cl, TieuDe[1]              

Các lệnh sau đây sử dụng hàm 02/21h để in xâu kí tự vừa nhập ra lại màn hình:

            Lea            DI, TieuDe

            Mov          Cx, 0

            Mov          Cl, TieuDe[1]

            Add           DI, 2

            Mov          Ah, 02

      Lap_In:

            Mov          Dl, DS:[DI]

            Int             21h

            INC           DI

            Loop         Lap_In  

Các lệnh sau đây sử dụng hàm 09/21h để in xâu kí tự vừa nhập ra lại màn hình:

            Mov          Ax, Seg TieuDe

            Mov          Ds, Ax

            Lea            Dx, TieuDe

            Add           Dx, 2

            Mov          DI, Dx

            Mov          Cx, 0

            Mov          Cl, TieuDe[1]

            Add           DI, Cx

            Mov          Byte PTR DS:[DI], ‘$’

            Mov          Ah, 09h

            Int             21h

Ví dụ 3: Chương trình dạng COM sau đây sử dụng hàm 0Ah ngắt 21h để nhập một xâu kí tự vào biến Buff. Sau đó sử dụng hàm 09h ngắt 21h để in xâu kí tự vừa nhập ra lại màn hình.

 Để đơn giản, chương trình khai báo biến Buff gồm toàn kí tự ‘$’, nhờ đó, khi dùng hàm 09h/21h để in ra chương trình không cần đưa thêm kí tự ‘$’ vào cuối xâu nhập, mà chỉ cần trỏ DS:DX về đầu vùng/xâu kí tự cần in ra.

        .Model       small

        .Code

                    ORG       100h

          Start:

                        JMP    Main

              TBN    DB      'Nhap vao mot xau ki tu: $'

              TBX    DB      0Ah,0Dh,'Xau vua nhap: $'

              Buff    DB      100,0,100 Dup ('$')        

          Main          Proc

                    Mov        Ah,  09h

                    Lea          Dx, TBN

                    Int           21h

                    ;--------------------------

                    Mov        Ah, 0Ah

                    Lea          Dx, Buff

                    Int           21h

                    ;-------------------------

                    Mov        Ah,  09h

                    Lea          Dx, TBX

                    Int           21h

                    ;-------------------------

                    Mov        Ah,  09h

                    Lea          Dx, Buff

                    Add         Dx, 2

                    Int           21h

                    ;-------------------------

                    Int           20h

          Main          Endp

                    End         Start

Một số ví dụ:

Ví dụ 1: Giả sử hàm X của ngắt Y khi được gọi sẽ trả về trong cặp thanh ghi ES:SI địa chỉ của vùng nhớ chứa tên của nhà sản xuất (nhãn hiệu) của vi xử lý đang sử dụng trên máy tính hiện tại, tên này dài không quá 8 kí tự.

Các lệnh sau đây sẽ in tên nhà sản xuất nói trên ra màn hình:

                    Mov        Ah, X                          ; hàm cần gọi được đưa vào thanh ghi ah

                    Int           Y                                 ; gọi ngắt Y với hàm X

                    ;--------------------      

                    Mov        Cx, 8                           ; tên dài không quá 8 kí tự

                    Mov        Ah, 02                                    ; dùng hàm 02/21h để in kí tự ra nàm hình

             LapIn:

                    Mov        Dl, Byte PTR ES:[SI]

                    Int           21h

                    INC         SI                                 ; đến kí tự tiếp theo

                    Loop       LapIn

                    ;------------------------------------

Ví dụ 2: Giả sử hàm X của ngắt Y khi được gọi với AL = 1 (được gọi là hàm con) sẽ trả về trong cặp thanh ghi ES:DI địa chỉ của vùng nhớ chứa ngày-tháng-năm sản xuất ROM-BIOS đang sử dụng trên máy tính hiện tại. Đồng thời hàm/ngắt này cũng cho biết số kí tự trong xâu ngày-tháng-năm trong thanh ghi BX.

Các lệnh sau đây sẽ in xâu ngày-tháng-năm nói trên ra màn hình:

                    Mov        Ah, X                          ; hàm cần gọi được đưa vào thanh ghi ah

                    Mov        Al, 1                            ; gọi hàm X với Al = 1

                    Int           Y                                 ; gọi ngắt Y với hàm X

                    ;--------------------

                    Mov        Cx, Bx                        ; đưa số kí tự của xâu ngày-tháng-nămvào Cx

                    Mov        Ah, 02                                    ; dùng hàm 02/21h để in kí tự ra nàm hình

             LapIn:

                    Mov        Dl, Byte PTR ES:[DI]

                    Int           21h

                    INC         DI                                ; đến kí tự tiếp theo

                    Loop       LapIn

                    ;-------------------------------------

Ví dụ 3: Hàm 39h của ngắt 21h được sử dụng để tạo thư mục con trên đĩa. Hàm này quy định: DS:DX chứa xâu tên thư mục cần tạo, bao gồm cả đường dẫntìm đến thư mục này, xâu này kết thúc bởi trị 0. Nếu việc tạo không thành công thì Cf = 1, khi đó thanh ghi Ax sẽ chứa mã lỗi.

Các lệnh sau đây sẽ tạo ra thư mục con BTCB trong thư mục ASSEM trên thư mục gốc ổ đĩa D.

Chương trình phải khai báo biến TenTM, chứa xâu tên thư mục cần tạo như sau:

            TenTM           DB      ‘D:\ASSEM\BTCB’,0

Các lệnh:

            Mov    Ax, Seg TenTM

            Mov    DS, Ax

            Mov    Dx, Offset TenTM

            Mov    Ah, 39h

            Int       21h

            ;-------------------------

            Jc        TB_Loi                                   ; nếu CF = 1 thì việc tạo bị lỗi

                      <In ra thong bao hoan thanh>

            Jmp     KetThuc

       TB_Loi:

                    <In ra thong bao khong tao duoc thu muc>

        KetThuc:

Tập lệnh assembly của Intel 8086/8088 (Phần 4)

Một số ví dụ minh họa:

Ví dụ 1: Giả sử tại địa chỉ 0100:0120 trong bộ nhớ có chứa một mảng dữ liệu, gồm 100 ô nhớ, mỗi ô là 1 word. Hãy tính tổng nội dung của các ô nhớ này, kết quả chứa trong thanh ghi Dx.

                    Mov        Ax, 0100                  

                    Mov        DS, Ax                          ; trỏ cặp thanh ghi DS:DI về

                    Mov        DI, 0120                       ; đầu vùng nhớ cần truy xuất (0100:0120)

                    ;------------------------            

                    Mov        Dx, 0                            ; chuẩn bị Dx để lưu tổng

                    Mov        Cx, 100                         ; lặp 100 lần vì mảng gồm 100 ô nhớ

             Lap_TT:

                    Add         Dx, DS:[DI]                           ; cộng thêm n/dung ô nhớ chỉ bởi DS:DI vào Dx

                    Add         DI, 2                                      ; chỉ đến ô nhớ kế tiếp (vì ô nhớ word nên tăng 2)

                    Loop       Lap_TT                      ; lặp lại đủ 100 lần (duyệt qua đủ 100 ô nhớ)

                    ;-----------------------------------

Kết thúc đoạn lệnh trên tổng nội dung của 100 word nhớ bắt đầu tại địa chỉ 0100:0120 trong bộ nhớ được lưu vào thanh ghi Dx. Ở đây chúng ta bỏ qua khả năng tràn dữ liệu trong thanh ghi kết quả Dx.

Ví dụ 2: Các lệnh sau đây sẽ copy 100 word dữ liệu từ vùng nhớ bắt đầu tại địa chỉ 0100:0120 sang vùng nhớ bắt đầu tại 0100:0500:

                    Mov        Ax, 0100

                    Mov        DS, Ax

                    Mov        SI, 0120                      ; trỏ DS:SI về vùng nhớ nguồn 0100:0120

                    Mov        DI, 0500                     ; trỏ DS:DI về vùng nhớ địch 0100:0500

                    ;--------------------

                    Mov        Cx, 100

            Lap_Copy:

                    Mov        Ax, DS:[SI]                  ; lưu tạm nội dụng word nhớ tại DS:SI  vào Ax

                    Mov        DS:[DI], Ax                  ; ghi giá trị Ax và word nhớ tại DS:DI

                    Add         SI, 2                            ; đến word nhớ tiếp theo

                    Add         DI, 2

                    Loop       Lap_Copy

             ;-------------------------

Hai vùng bộ nhớ đã cho đều ở trong cùng một segment nên ở đây ta chỉ cần sử dụng một thanh ghi đoạn dữ liệu DS.    

Ví dụ 3: Các lệnh sau đây sẽ tính tổng nội dung của 100 ô nhớ (100 byte nhớ) trong bộ nhớ, bắt đầu tại địa chỉ 0A00:0120. Kết quả được lưu vào word nhớ ngay trước vùng nhớ này:

                    Mov        Ax, 0A00h

                    Mov        ES, Ax

                    Mov        SI, 0120h                    ; trỏ DS:SI về vùng nhớ nguồn 0A00:0120

                    Mov        DI, SI                          ; trỏ DS:DI về vùng nhớ nguồn 0A00:0120

                    Sub         DI,2                             ; trỏ DS:DI về word nhớ trước vùng nhớ nguồn

                    ;------------------------

                    Mov        Cx, 100        

                    Mov        Dx, 0                                            ; DX chứa tổng

              TTong:

                    Add         Dx, Byte PTR ES:[SI]                  ; cộng n.d của byte nhớ tại ES:SI vàoDX

                    Inc           SI                                               ; ô nhớ byte

                    Loop       TTong

                    ;----------------------------------------

                    Mov        Word PTR ES:[DI], DX

                    ;----------------------------------------

Trong đoạn lệnh trên chúng ta sử dụng toán tử PTR để định kiểu ô nhớ cần truy xuất. Lệnh Add    Dx, Byte PTR ES:[SI]: lấy nội dung của byte nhớ tại ô nhớ được chỉ bởi ES:SI cộng thêm vào thanh ghi DX. Nếu ta viết Add    Dx, ES:[SI] thì hệ thống lấy giá trị cả 1 word tại ES:SI cộng thêm vào DX (vì DX là thanh ghi word (16 bít), điều này không đúng với thực tế, vì chúng ta đang cần truy xuất đến các byte nhớ. Trong trường hợp này, nếu không dùng toán tử PTR thì lệnh Add phải viết như sau: Add    DL, ES:[SI], khi đó hệ thống chỉ lấy giá trị cả 1 byte tại ES:SI cộng thêm vào DL (vì DL là thanh ghi byte: 8 bít),

Ví dụ 4: Các lệnh sau đây sẽ copy toàn bộ 20 kí tự Ascii từ biến Xau1 vào biến Xau2. Giả sử Xau1 và Xau2 đã được khai báo trước như sau:

Xau1               DB      ‘Khoa CNTT – DHKH Hue’

Xau2               DB      20 Dup (‘ ’)

Các lệnh:

                    Mov        Ax, @Data

                    Mov        DS, Ax

                    Lea          SI, Xau1

                    Lea          DI, Xau2

                    ;------------------------

                    Mov        Cx, 20

            Lap_Copy:

                    Mov        Al, Byte PTR DS:[SI]

                    Mov        Byte PTR DS:[DI], Al

                    Inc           SI

                    Dec         DI

                    Loop       Lap_Copy    

                    ;-------------------------------------

Các biến của chương trình hợp ngữ được khai báo trong đoạn Data. Hai lệnh đầu tiên ở trên có tác dụng lấy địa chỉ segment của đoạn Data đưa vào thanh ghi đoạn DS. Do đó, DS sẽ chứa địa chỉ segment của hai biến Xau1 và Xau2. Hay cụ thể: DS:SI trỏ về biến Xau1 và DS:DI trỏ về Xau2.

Ví dụ 5: Chương trình sau đây: Để in một xâu kí tự từ trong chương trình ra màn hình văn bản đơn sắc (môi trường MSDOS).

Chú ý:

-     BIOS và MSDOS đã cung cấp nhiều hàm/ngắt để chương trình hợp ngữ ghi một kí tự hoặc một xâu kí tự ra màn hình, tại vị trí hiện tại của con trỏ màn hình hoặc tại một tọa độ màn hình xác định nào đó. Kỹ thuật này được gọi là kỹ thuật ghi ra màn hình gián tiếp thông qua các hàm/ngắt màn hình.

-     Biết rằng, tất cả những thông tin mà chúng ta thấy được trên màn hình của một máy tính đều được lưu trữ trong bộ nhớ màn hình của máy tính đó, theo một cách nào đó. Tức là, nọi thao tác ghi/đọc trên màn hình đều phải thông qua bộ nhớ màn hình. Như vậy: Muốn ghi một xâu kí tự ra màn hình chương trình có thể ghi nó vào bộ nhớ màn hình. Muốn đọc một xâu kí tự từ màn hình chương trình có thể đọc nó từ bộ nhớ màn hình. Kỹ thuật này được gọi là kỹ thuật truy xuất trực tiếp bộ nhớ màn hình.

-     Mỗi hệ điều hành, mỗi chế độ màn hình sử dụng một đoạn bộ nhớ xác định (thường là khác nhau) cho bộ nhớ màn hình. Và cách tổ chức lưu trữ thông tin trên màn hình trong bộ nhớ màn hình cũng khác nhau với các hệ điều hành, các chế độ màn hình khác nhau.

-     Trên môi trường hệ điều hành MSDOS, bộ nhớ màn hình của chế độ nàm hình đơn sắc 25 dòng (0 đến 24) x 80 cột (0 đếm 79) được lưu trữ tại segment nhớ 0B800, bắt đầu tại offset 0000.

-     Tổ chức lưu trữ thông tin trên màn hình trong bộ nhớ màn hình loại này như sau: Mỗi kí tự trên nàm hình chiếm 1 word trong bộ nhớ màn hình: byte thấp chứa mã ASCII của kí tự, byte cao chứa thuộc tính (màu chữ, màu nền,...) của kí tự đó. Từng dòng kí tự trên màn hình, từ trái sang phải (từ cột 0 đến cột 79), được lưu tữ lần lượt tại các offset liên tiếp nhau trong bộ nhớ màn hình: 80 kí tự của dòng 0 được lưu trữ tại 80 word đầu tiên, 80 kí tự của dòng 1được lưu trữ tại 80 word tiếp theo,... , 80 kí tự của dòng 79ược lưu trữ tại 80 word cuối cùng trong bộ nhớ nàm hình. Như vậy ta dễ dàng tính ra được offset trong bộ nhớ màn hình, tương ứng với một kí tự trên màn hình khi ta biết được tọa độ (dòng, cột) của nó. Cụ thể, offset của kí tự tạo tọa độ (Dòng, Cột) được tính theo công thức sau:

                                    (80*(Dong - 1) + (Cot - 1))*2      

-      Bộ nhớ màn hình loại này có kích thước là 25 x 80 x 2 (byte) = 40000 byte.

Tập lệnh assembly của Intel 8086/8088 (Phần 3)

5. Lệnh Mul và Div

Cú pháp:

Mul     [Toán hạng nguồn]
IMul   [Toán hạng nguồn]
Div      [Toán hạng nguồn]
IDiv    [Toán hạng nguồn]
Trong đó: [Toán hạng nguồn]có thể là thanh ghi hay ô nhớ. Với các lệnh nhân: [Toán hạng đích] ngầm định là thanh ghi Al hoặc Ax. Với các lệnh chia: [Toán hạng đích] là một trong các thanh ghi đa năng Ax, Bx,...

Tác dụng:

-     Lệnh Mul (Multiply): Thực hiện phép nhân trên số không dấu. Nếu [Toán hạng nguồn] là toán hạng 8 bít thì lệnh sẽ nhân nội dung của [Toán hạng nguồn] với giá trị thanh ghi AL, kết quả 16 bít chứa ở thanh ghi Ax.  

Nếu [Toán hạng nguồn] là toán hạng 16 bít thì lệnh sẽ nhân nội dung của [Toán hạng nguồn] với giá trị thanh ghi Ax, kết quả 32 bít chứa ở cặp thanh ghi Dx:Ax, phần thấp ở Ax, phần cao ở Dx. Nếu phần cao của kết quả (AH hoặc DX) bằng 0 thì các cờ CF = 0 và OF = 0.

-     Lệnh IMul (Interger Multiply): Tương tự lệnh Mul nhưng thực hiện phép nhân trên hai số có dấu. Kết quả cũng là một số có dấu.

     -     Lệnh Div (Divide): Thực hiện phép chia trên số không dấu. Nếu [Toán hạng nguồn] là toán hạng 8 bít thì lệnh sẽ lấy giá trị của thanh ghi Ax (số bị chia) chia cho [Toán hạng nguồn] (số chia), kết quả thương số chứa trong thanh ghi Al, số dư chứa trong thanh ghi Ah.    

Nếu [Toán hạng nguồn] là toán hạng 16 bít thì lệnh sẽ lấy giá trị của cặp thanh ghi Dx:Ax (số bị chia) chia cho [Toán hạng nguồn] (số chia), kết quả thương số chứa trong thanh ghi Ax, số dư chứa trong thanh ghi Dx.

Nếu phép chia cho 0 xảy ra hay thương số vượt quá giới hạn của thanh ghi AL (chia 8 bít) hay Ax (chia 16 bít) thì CPU sẽ phát sinh lỗi “Divice overflow”.

-     Lệnh Idiv (Integer Divide): Tương tự lệnh  Div nhưng thực hiện phép chia trên hai số có dấu. Kết quả cùng là các số có dấu.

Ví dụ 1:

-             Mul         Bl                    ; Ax ßAL * Bl: số bị nhân ngầm định trong Al      

-             Mul         Bx                   ; Dx:Ax ßAx * Bx: số bị nhân ngầm định trong Ax

-             Idiv         Bl                    ; Ax/Bl, thương số chứa trong Al, số dư chứa trong Ah

-             Idiv         Bx                   ; Dx:Ax/Bx, thương số chứa trong Ax, số dư trong Dx

Ví dụ 2: Dãy các lệnh dưới đây sẽ thực hiện phép gán A = 4*A – 3*B, trong đó A và B là các biến kiểu word:

                    Mov        Ax, 4                ; số nhân phải được chứa trong Ax

                    IMul        A                      ; thực hiện phép nhân

                    Mov        Bx, Ax              ; lưu tạm kết quả vào Bx      

                    Mov        Ax, 3                ; Ax = 3

                    Imul        B                     ; Ax = Ax * B

                    Sub         Bx, Ax          

                    Mov        A, Bx                ; đặt kết quả cuối cùng vào A

Trong trường hợp này ta đã giả sử hiện tượng tràn đã không xảy ra và kết quả phép nhân chỉ chứa trong thanh ghi Ax.  

Ví dụ 3: Các lệnh sau đây thực hiện phép: chia -123 cho 24:

                    Mov        Ax, -123         ; đặt số bị chia vào Ax

                    Mov        Bl, 24              ; đặt số chia vào Bl (thanh ghi 8 bít)

                    Idiv         Bl                    ; chia Ax cho Bl, kết quả chứa ở Al và Ah

Ví dụ 4: Dãy lệnh dưới đây sẽ thực hiện phép gán A = N! (tính N giai thừa). A là một biến word:

                    Mov        Ax, 1               ; chuẩn bị Ax để lưu kết quả

                    Mov        Cx, N              ; tính N!

               LapNhan:                        

                    Mul         Cx                   ; Ax ßAx * Cx

                    Loop       LapNhan    

                    ;---------------------------

                    Mov        A, Ax              ; trả kết quả vào biến A

Trong trường hợp này chúng ta giả sử kết quả không vượt quá gới hạn 16 bít.

Chúng ta đã biết: N! = 1 nếu N = 1, N! = N*(N-1)*(N-2)*...*1 nếu N>1, điều này hoàn toàn phù hợp với sự thay đổi của thanh ghi CX trong lệnh lặp Loop. Do đó, ở đây ta có thể sử dụng CX như là N trong biểu thức tính giai thừa.  

Chú ý: Hợp ngữ cung cấp lệnh AAM (Ascii Adjust for Multiple) để điều chỉnh kết quả phép nhân trên 2 số BCD dạng không dồn. Và lệnh AAD (Ascii Adjust for Division) để điều chỉnh kết quả phép chia trên 2 số BCD dạng không dồn. Ngoài ra còn có lệnh CBW (Convert Byte to Word) và lệnh CWD (Convert Word to Doubleword) để hỗ trợ cho phép chia trên các số có dấu [2 – 187-200].

Lệnh IMul của vi xử lý Intel 80286 cho phép ghi rõ [Toán hạng đích], [Toán hạng nguồn] trong câu lệnh, các lệnh này có thể có đến 3 toán hạng [1 - 541].

6. Lệnh logic: NOT – AND – OR – XOR – TEST

Trước khi tìm hiểu về các lệnh logic chúng ta xem lại kết quả thực hiện các phép tính logic trên 2 bít nhị phân A và B thông qua bảng sau đây:

A

B

A And B

A Or B

A Xor B

NotA

0

0

0

0

0

1

0

1

0

1

1

1

1

0

0

1

1

0

1

1

1

1

0

0


Bảng trên cho thấy: Với phép And: kết quả = 1 chỉ khi cả hai bít = 1; Với phép Or: kết quả = 0 chỉ khi cả hai bít = 0; Với phép Xor: kết quả = 0 khi hai bít giống nhau, kết quả = 1 khi hai bít khác nhau. Với phép Not: 0 thành 1, 1 thành 0.    

Cú pháp:

Not     [Toán hạng đích]
And     [Toán hạng đích], [Toán hạng nguồn]
Or       [Toán hạng đích], [Toán hạng nguồn]
Xor     [Toán hạng đích], [Toán hạng nguồn]
Test    [Toán hạng đích], [Toán hạng nguồn]
Trong đó: [Toán hạng đích], [Toán hạng nguồn] có thể là hằng số (trực hằng), biến, thanh ghi hay địa chỉ ô nhớ. [Toán hạng đích] không thể là hằng số.

Tác dụng: Mỗi lệnh logic thực hiện phép tính logic tương ứng trên các bít (tương ứng về vị trí) của [Toán hạng đích] và [Toán hạng nguồn], kết quả được ghi vào lại [Toán hạng đích]. Riêng lệnh Not, thực hiện phép đảo bít ngay trên các bít của [Toán hạng đích]. Hầu hết các lệnh logic đều ảnh hưởng đến các cờ CF, OF, ZF,...

-     Lệnh Not (Logical Not): Thực hiện việc đảo ngược từng bít trong nội dung của [Toán hạng đích]. Lệnh này không làm ảnh hưởng đến các cờ.

Lệnh Not thường được sử dụng để tạo dạng bù 1 của [Toán hạng đích].      

-     Lệnh And (Logical And): Thực hiện phép tính logic And trên từng cặp bít (tương ứng về vị trí) của [Toán hạng nguồn] với [Toán hạng đích], kết quả lưu vào [Toán hạng đích].

Lệnh And thường được sử dụng để xóa (= 0) một hoặc nhiều bít xác định nào đó trong một thanh ghi.

-     Lệnh Or (Logical Inclusive Or):Thực hiện phép tính logic Or trên từng cặp bít (tương ứng về vị trí) của [Toán hạng nguồn] với [Toán hạng đích], kết quả lưu vào [Toán hạng đích].

Lệnh Or thường dùng để thiết lập (= 1) một hoặc nhiều bít xác định nào đó trong một thanh ghi.

-     Lệnh Xor (eXclusive OR):Thực hiện phép tính logic Xor trên từng cặp bít (tương ứng về vị trí) của [Toán hạng nguồn] với [Toán hạng đích], kết quả lưu vào [Toán hạng đích].

Lệnh Xor thường dùng để so sánh (bằng nhau hay khác nhau) giá trị của hai toán hạng, nó cũng giúp phát hiện ra các bít khác nhau giữa hai toán hạng này.

-     Lệnh Test: Tương tự như lệnh And nhưng không ghi kết quả vào lại [Toán hạng đích], nó chỉ ảnh hưởng đến các cờ CF, OF, ZF,...

Ví dụ 1:

                    Mov        Al,0                 ; Al ß0

                    Not          Al                    ; Al = Not Al. Tức là Al = 0FFh

Ví dụ 2: Cho AL = (10010011)2, BL = (11001100)2.

-             And         Al, Bl              ; Al ß 10010011 And 11001100. Al =

-             And         Al, 0                ; Al ß 10010011 And 0. Al =

-             Or            Bl, Al              ; Bl ß 11001100 Or 10010011. Al =

-             Or           Bl, 4                ; Bl ß 11001100 Or 100. Al =

-             Xor         Al, Bl              ; Al ß 10010011 Xor 11001100. Al =

-              Xor         Bl, Bl              ; Bl ß 11001100 Xor 11001100. Bl = 00000000

Ví dụ 3: Để xóa nội dung thanh ghi nào đó, trong hợp ngữ ta có thể sử dụng một trong các lệnh sau đây:

-             Mov        Ax, 0

-             Sub         Ax, Ax

-             Xor         Ax, Ax            ; các cặp bít giống nhau thì đều = 0

Ví dụ 5: Lệnh sau đây sẽ xóa (= 0) các bít 3 và 6 của thanh ghi AL, các bít khác giữ nguyên giá trị:

-             And         AL, 10110111b        ; AL ßAL And 10110111

Trong trường hợp này: dãy bít  10110111 được gọi là dãy bít mặt nạ, các bít 3 (= 0) và 6 (= 0) được gọi là các bít mặt nạ. Như vậy muốn làm cho bít nào = 0 ta cho bít mặt nạ tương ứng với nó = 0, các bít còn lại trong dãy bít mặt nạ đều = 1.      

Ví dụ 6:  Lệnh sau đây sẽ thiết lập (= 1) các bít 3 và 6 của thanh ghi AL, các bít khác giữ nguyên giá trị:

-             Or            AL, 01001000b        ; AL ßAL Or 01001000

Trong trường hợp này: dãy bít 01001000 được gọi là dãy bít mặt nạ, các bít 3 (= 1) và 6 (= 1) được gọi là các bít mặt nạ. Như vậy muốn làm cho bít nào = 1 ta cho bít mặt nạ tương ứng với nó = 1, các bít còn lại trong dãy bít mặt nạ đều = 0.      

Ví dụ 7: Lệnh sau đây sẽ kiểm tra bít 12 của thanh ghi AX là = 0 hay = 1:

-             And         AX, 0001000000000000b              ; AX ßAX And 0001000000000000

Với dãy bít mặt nạ như trên, nếu bít 12 của Ax = 0 thì kết quả: Ax = 0, nếu bít 12 của Ax = 1 thì kết quả: Ax <> 0.

Cách dùng lệnh And như trên để kiểm tra bít ít được sử dụng, vì nó làm thay đổi giá trị của thanh ghi cần kiểm tra (điều này có thể khắc phục bằng lệnh Test) và phải thêm bước kiểm tra giá trị của Ax (= 0 hay <> 0) mới biết được kết quả kiểm tra. Ngoài ra, nó cũng chỉ kiểm tra được 1 bít.

Trong thực tế người ta thường sử dụng kết hợp giữa các lệnh dịch bít, lệnh quay bít, lệnh nhảy,... để kiểm tra các bít trong một thanh ghi.

7. Lệnh chuyển dữ liệu qua cổng: IN và OUT

Cú pháp:

IN           AL, <Địa chỉ cổng>      
OUT       <Địa chỉ cổng>, AL
Trong đó:<Địa chỉ cổng> chính là số hiệu cổng (port) mà lệnh nhận nhiệm vụ trao đổi dữ liệu qua nó. Địa chỉ cổng có thể được ghi trực tiếp dưới dạng một hằng số hoặc được ghi thông qua thanh ghi Dx.

Tác dụng:

-     LênhIn (Input): Đọc một lượng dữ liệu 8 bít từ cổng được chỉ ra ở <Địa chỉ cổng> đưa vào lưu trữ trong thanh ghi AL.

Nếu địa chỉ cổng nằm trong giới hạn từ 0 đến FF (hệ thập lục phân) thì có thể viết trực tiếp trong câu lệnh, nếu địa chỉ cổng lớn hơn FF thì ta phải dùng thanh ghi Dx để chỉ định địa chỉ cổng.

-     LệnhOut (Output): Gởi một lượng dữ liệu 8 bít từ thanh ghi AL ra cổng được chỉ ra ở <Địa chỉ cổng>. Tương tự lệnh In, địa chỉ cổng có thể được viết trực tiếp trong câu lệnh hoặc thông qua thanh ghi Dx.

Ví dụ 1:

-             In             Al, 40h                       ;                    

-             Mov        Dx, 3B0h                   ;

               In             Al, Dx                         ;

Ví dụ 2:

-             Out          40h, Al                       ;                    

-             Mov        Dx, 3B0h                   ;

               Out          Dx, Al                         ;

Ví dụ 3:

Các khai báo hằng:

                    DAT        EQU                13h     ;

                    POR        EQU                7Ch     ;

Các lệnh:

        Mov        Al, POR          ;

        Mov        Bl, DAT          ;

        Out          Bl, Al              ;

Tập lệnh assembly của Intel 8086/8088 (Phần 2)

3. Lệnh LOOP

Cú pháp:

             Loop      <Nhãn đích>                                                                    

Trong đó: <Nhãn đích> là một nhãn lệnh và nó phải đứng trước lệnh lặp Loop không quá 126 byte.

Tác dụng: Khi gặp lệnh này chương trình sẽ lặp lại việc thực hiện các lệnh sau <Nhãn lệnh> đủ n lần, với n được đặt trước trong thanh ghi CX. Sau mỗi lần lặp CX tự động giảm 1 đơn vị (Cx = Cx - 1) và lệnh lặp sẽ dừng khi Cx = 0.

Lệnh Loop thường được sử dụng để cài đặt các đoạn chương trình lặp với số lần lặp xác định, được cho trước trong thanh ghi Cx (tương tự các vòng lặp For trong các ngôn ngữ lập trình bậc cao).

Ví dụ 1: Xem đoạn lệnh sau đây:

                    Mov        Ax, 6

                    Mov        Cx, 4                           ; lặp lại 4 lần

         Lap:    Add         Ax, 10                        ; cộng thêm 10 vào Ax          

                    Loop       Lap                             ; lặp lại việc cộng 10 vào Ax đủ 4 lần

Kết thúc đoạn lệnh trên: Ax = 46 (cụ thể: Lần 1: Ax = 6 + 10; Lần 2: Ax = 16 + 10; Lần 3: Ax = 26 + 10; Lần 4: Ax = 36 + 10 = 46).

Ví dụ 2: Xem đoạn lệnh sau đây:

                    Mov        Ax, 6            

                    Mov        Bx, 3            

                    Mov        Cx, 4               ; lặp lại 4 lần

            Lap_TT:

                    Add         Ax, Bx              ; cộng thêm giá trị thanh ghi Bx vào thanh ghi Ax

                    Inc           Bx                    ; tăng Bx lên 1 đơn vị

                    Loop       Lap_TT              ; lặp lại các lệnh sau nhãn lệnh Lap_TT đủ 4 lần

                    ;-----------------------------          ; sau lệnh lặp này Ax = 24, Bx = 7

                    Mov        Dx, Ax                ; Dx ß Ax

                    Mov        Cx, Bx                ; Cx = 7, sau Loop Cx = 0, được thay bằng Bx = 7

Kết thúc đoạn lệnh trên: Ax = 24 (Lần 1: Ax = 6 + 3;Lần 2: Ax = 9 + 4; Lần 3: Ax = 13 + 5; Lần 4: Ax = 18 + 6) và Dx = Ax = 24.

Khi gặp lệnh Loop     Lap_TT chương trình sẽ quay lại (nếu Cx <> 0) thực hiện lệnh Add      Ax, Bx (Lap_TT là nhãn của lệnh này), tất nhiên khi đó nó cũng phải thực hiện lại lệnh Inc      Bx. Dó đó, có thể nói lệnh Loop này thực hiện vòng lặp cho cả hai lệnh Add và Inc. Đó cũng chính là lý do mà người ta thường viết nhãn của các lệnh phải được lặp theo kiểu như trên (nhãn lệnh và lệnh không cùng trên một dòng).

Ví dụ 3: Xem đoạn lệnh sau đây:

                    Mov        Dx, 6            

                    Mov        Cx, 5               ; lặp lại 5 lần

            TT:

                    Add         Dx, Cx            ; cộng thêm giá trị thanh ghi Cx vào thanh ghi Dx

                    Loop       TT                   ; lặp lại các lệnh sau nhãn lệnh TT đủ 5 lần

                    ;-----------------------------;

                    Mov        Bx, Cx          

Kết thúc đoạn lệnh trên Bx = Cx = 0 (khi Cx = 0 thì vòng lặpLoop TT kết thúc) và Dx = 21 (Lần 1: Dx = Dx + Cx = 6 + 5;Lần 2: Dx = Dx + Cx = 11 + 4; Lần 3: Dx = Dx + Cx = 15 + 3; Lần 4: Dx = Dx + Cx = 18 + 2;Lần 5: Dx = Dx + Cx = 20 + 1 = 21).

Ví dụ 4: Các lệnh sau đây thực hiện phép gán:

                    Ax = 2 + 4 + ...+ 100

                    Mov        Ax, 0

                    Mov        Bx, 2

                    Mov        Cx, 50

              Lap_TT:

                    Add         Ax, Bx

                    Add         Bx, 2

                    Loop       Lap_TT

Ví dụ 5: Giả sử tại địa chỉ offset 100 trong đoạn nhớ Data (được chỉ bởi thanh ghi đọan DS) có chứa một mảng dữ liệu, gồm 100 ô nhớ, mỗi ô là một byte. Hãy cộng thêm 50 đơn vị vào tất cả các ô nhớ trong mảng này.

                    Mov        DI, 0100                     ; trỏ cặp thanh ghi DS:DI về

                                                                       ; vùng nhớ cần truy xuất (DS:0100)

                    ;------------------------            

                    Mov        Cx, 100                      ; lặp 100 lần vì mảng gồm 100 ô nhớ

             Lap_TangThem:

                    Mov        Dl, DS:[DI]                 ; lấy nôi dung ô nhớ chỉ bởi DS:DI lưu vào DL

                    Add         Dl, 50                        ; cộng thêm 50 vào Dl

                    Mov        DS:[DI], Dl                  ; đặt giá trị đã tăng thêm vào lại ô nhớ DS:DI

                    Inc           DI                             ; chỉ đến ô nhớ kế tiếp (vì ô nhớ byte nên tăng 1)

                    Loop       Lap_TangThem       ; lặp lại đủ 100 lần (duyệt qua đủ 100 ô nhớ)

Trong trường hợp này ta có thể sử dụng lệnh Add      DS:[DI], 50 để tăng trực tiếp nội dung của ô nhớ, hợp ngữ cho phép điều này. Nhưng cách đã viết thường được áp dụng hơn, vì tính tổng quát của nó. Nói chung, chúng ta nên hạn chế tác động trực tiếp lên nôi dung của ô nhớ.

Ví dụ 6: Giả sử tại địa chỉ 0100:0C00 trong bộ nhớ có chứa một xâu kí tự gồm 50 kí tự (tức là, gồm 50 ô nhớ, mỗi ô 1 byte). Hãy copy xâu kí tự này sang vùng nhớ bắt đầu tại địa chỉ 0200:0100.

                    Mov        Ax, 0100                  

                    Mov        DS, Ax                        ; trỏ cặp thanh ghi DS:SI về

                    Mov        SI, 0C00                     ; đầu vùng nhớ chưa xâu cần copy (0100:0C00)

                    Mov        Ax, 0200                  

                    Mov        ES, Ax                        ; trỏ cặp thanh ghi ES:DI về

                    Mov        DI, 0100                     ; vùng nhớ chứa xâu kết quả copy 0200:0100

                    ;------------------------            

                    Mov        Cx, 50                         ; lặp 50 lần vì xâu gồm 50 kí tự

             Lap_Copy:

                    Mov        Bl, DS:[SI]                 ; mượn Bl để chuyển tường kí tự từ ô nhớ được

                    Mov        ES:[DI], Bl                 ; chỉ bởi DS:SI sang ô nhớ được chỉ bởi ES:DI

                    Inc           SI                                 ; chuyển đến kí tự tiếp theo

                    Inc           DI                              

                    Loop       Lap_Copy                 ; lặp lại đủ 50 lần (để copy  đủ 50 kí tự)

Trong ví dụ này, vùng nhớ chứa xâu kí tự cần copy (vùng nhớ nguồn) và vùng nhớ chứa kết quả copy (vùng nhớ đích) nằm ở hai đoạn (segment) nhớ khác nhau, do đó, ta phải sử dụng hai thanh ghi đoạn dữ liệu khác nhau: DS và ES.

Qua 2 ví dụ 5 và 6 ta có thể rút ra nguyên tắc cơ bản để truy xuất dữ liệu trên một vùng nhớ/mảng nhớ:

Chú ý: Lệnh Loop thực hiện vòng lặp cho đến khi Cx = 0, vì thế nó được xem như lệnh lặp không điều kiện. Thực tế, hợp ngữ còn có các lệnh lặp có điều kiện, nó cho phép kết thúc vòng lặp trước khi Cx = 0 dựa vào một điều kiện nào đó. Cụ thể: lệnh LoopE (Loop while Equal): Chỉ lặp lại khi Cx <> 0 và cờ ZF = 1; lệnh LoopZ (Loop while Zero): tương tự LoopE; LoopNE (Loop while Not Equal): Chỉ lặp lại khi Cx <> 0 và cờ ZF = 0;... [2 - 154].

4. Lệnh LEA (LoadEffectiveAddress)

Cú pháp:

                        LEA     [Toán hạng đích],[Toán hạng nguồn]                

Trong đó: [Toán hạng đích]: Là các thanh ghi 16 bít. [Toán hạng nguồn]: Là địa chỉ của một vùng nhớ hay tên của một biến.

Tác dụng: Lệnh LEA có tác dụng chuyển địa chỉ offset của [Toán hạng nguồn] vào [Toán hạng đích]. Lệnh này thường được sử dụng để lấy địa chỉ offset của một biến đã được khai báo trong chương trình. Thanh ghi được sử dụng trong trường hợp này là thanh ghi cơ sở (BX) và thanh ghi chỉ mục (SI và DI).  

Ví dụ 1:

             Lea          Bx, DS:[0100]                ; chuyển thành phần địa chỉ offset (0100) vào Bx

             Lea          DI, XauKT                       ; chuyển địa chỉ offset của biến XauKT vào DI

                                                                        ; thao tác này thường được gọi là trỏ DI và đầu

                                                                        ; biến XauKT

Khi chương trình được nạp vào bộ nhớ để hoạt động thì các biến được khai báo trong chương trình sẽ được định vị (cấp phát vùng nhớ) tại một địa chỉ xác định trong vùng nhớ Data. Từ đây, để thao tác đến dữ liệu trên các biến của chương trình thì chương trình phải xác định được địa chỉ segment vào offset (hai thành phần của địa chỉ logic) của biến. Lệnh LEA ở trên chỉ lấy được địa chỉ offset của biến, để lấy được địa chỉ segment của nó ta có thể sử dụng lệnh Mov với toán tử Seg (tương tự có thể sử dụng lệnh Mov với toán tử Offset để lấy địa chỉ offset của biến). Ví dụ: Các lệnh sau lấy địa chỉ Segment:Offset của biến XauKT (hay trỏ DS:SI về đầu biến XauKT):  

                    Mov        Ax, Seg XauKT        ; đưa địa chỉ Segment của biến XauKT

                    Mov        DS, Ax                       ; vào thanh ghi DS

                    Mov        SI, Offset XauKT     ; đưa địa chỉ Offset của biến XauKT vào SI

Ví dụ 2: Giả sử biến TenGom (là biến kiểu byte) đã được khai báo như sau:

                        TenGom        DB      ‘Nguyen Kim Le Tuan’

Xem các lệnh sau đây (1):

                    Mov        Bx, 0

                    Mov        Al, TenGom[Bx]      ; Al =‘N’

                    Add         Bx, 7                           ;

                    Mov        Bl, TenGom[Bx]      ; Bl =‘K’

Xem các lệnh sau đây (2):

                    Lea          DI, TenGom

                    Mov        Al, [DI]                       ; Al =‘N’

                    Mov        Bl, [DI + 7]                ; Bl =‘K’

Ta có thể thấy, nhóm các lệnh (1) và nhóm các lệnh (2) là tương đương nhau về tác dụng của nó, nhưng (1): sử dụng trực tiếp tên biến để truy xuất đến các phần tử của nó; (2): sử dụng thanh ghi chỉ mục DI để truy xuất đến các phần tử của biến. Trong trường hợp này địa chỉ segment mặc định được chỉ bởi DS, điều này phù hợp với việc sử dụng địa chỉ gián tiếp thanh ghi chỉ mục.          

Ví dụ 3: Giả sử tại địa chỉ 0100:0C00 trong bộ nhớ có chứa một xâu kí tự gồm 50 kí tự (tức là, gồm 50 ô nhớ, mỗi ô 1 byte). Hãy copy xâu kí tự này vào một biến trong chương trình.

Với yêu cầu này chương trình phải khai báo một biến byte có độ lớn 50 byte:

            LuuTru           DB      50 Dup (‘ ‘)

                    Mov        Ax, 0100                  

                    Mov        DS, Ax                         ; trỏ cặp thanh ghi DS:SI về

                    Mov        SI, 0C00                     ; đầu vùng nhớ chưa xâu cần copy (0100:0C00)

                    ;--------------------------------

                    Mov        Ax, Seg   LuuTru       ; trỏ cặp thanh ghi ES:DI về

                    Mov        ES, Ax                         ; đầu biến LuuTru

                    Lea          DI,  LuuTru            

                    ;--------------------------------

                    Mov        Cx, 50

             Lap_Copy:

                    Mov        Bh, DS:[SI]                   ; mượn Bh để chuyển tường kí tự từ ô nhớ được

                    Mov        ES:[DI], Bh                   ; chỉ bởi DS:SI sang ô nhớ được chỉ bởi ES:DI

                    Inc           SI                                  ; chuyển đến kí tự tiếp theo

                    Inc           DI                              

                    Loop       Lap_Copy                 ; lặp lại đủ 50 lần (để copy  đủ 50 kí tự)

Chú ý: Hợp ngữ còn cung cấp các lệnh LDS (Load Pointer use DS) để lấy nội dung toán hạng bộ nhớ 32 bít đưa vào các thanh ghi 16 bít (mặc định 16 bít cao vào thanh ghi đoạn dữ liệu DS); và lệnh LES (Load Pointer use DS) tương tự LDS nhưng  mặc định 16 bít cao vào thanh ghi đoạn dữ liệu (thứ hai) ES [2 - 137].