Bài tập lập trình hợp ngữ asembly
Trang 1Bài thực hành số 1
Nhập môn
Mục đích
Làm quen với ngôn ngữ lập trình Assembly
Biết cách viết, dịch, chạy và chẩn lỗi (debug) một vài chương trình đơn giản
Tóm tắt lý thuyết
Hợp ngữ (assembler) là ngôn ngữ bậc thấp, giúp cho người lập trình không phải ghi nhớ mã máy (opcode) mà sử dụng các từ ngữ gợi nhớ (pseudo-code) gần với ngôn ngữ tự nhiên để miêu tả công việc cần thực hiện Tuy vậy, assembler rất gần với ngôn ngữ máy, đòi hỏi người lập trình phải hiểu biết tương đối đầy đủ về cấu trúc phần cứng máy tính
Với mỗi kiểu kiến trúc của bộ vi xử lý, có một bộ lệnh riêng, do đó, có một ngôn ngữ assembler riêng cho nó Ở đây, chúng ta nghiên cứu assembler cho các bộ vi xử lý Intel thuộc họ x86 Các chương trình sẽ được viết cho chế độ thực (real mode) trong DOS và được biên dịch bằng Turbo Assembler
Cấu trúc thông thường của một chương trình hợp ngũ
.model <Khai báo kiểu chương trình>
.stack <Khai báo kích thước ngăn xếp>
Trang 2Khai báo biến trong hợp ngữ
Cú pháp:
<tên biến> D<Kiểu DL> <giá trị khởi tạo>
hoặc
<tên biến> D<Kiểu DL> <số phần tử> dup(<giá trị khởi tạo>)
Các kiểu dữ liệu: B (1 byte), W (2 bytes), D (4 bytes)
Nếu không khởi tạo, dùng dấu hỏi “?”
Dịch, liên kết, chạy và chẩn lỗi chương trình từ dấu nhắc DOS
Cần có các file: tasm.exe (dịch), tlink.exe (liên kết), td.exe (chẩn lỗi) Các bước như sau:
B1 Thiết lập đường dẫn
path = %path%;<đường dẫn đến thư mục chứa các file kể trên>
B2 Biên dịch từ file ASM sang file OBJ
Tasm <tên file chương trình>.ASM
B3 Biên dịch từ file OBJ sang file EXE
Tlink <tên file>.OBJ
%1
Trang 3(%1 là lấy tham số thứ nhất trong command line)
Sau đó để biên dịch, liên kết và thực thi chương trình hello.ASM ta chỉ cần gõ :
RunASM hello
Công cụ EditPlus
Đây là công cụ soạn thảo văn bản tiện dụng, cho phép tự động đổi màu chữ theo cú pháp Ngoài ra còn có thể thiết đặt phím tắt để gọi các tiện ích khác Để dùng cho soạn thảo chương trình assembler, cần copy file định nghĩa cú pháp vào thư mục cài đặt và đăng kí sử dụng nó cho những file có tên mở rộng “.asm”
B0 Cấu hình Edit Plus: xem trong file hướng dẫn
B1 Biên dịch file ASM : nhấn Ctrl + 1 sẽ biên dịch file đang soạn thảo thành OBJ
B2 Liên kết : nhấn Ctrl + 2 sẽ biên dịch file OBJ thành EXE
B3 Chạy chương trình : nhấn Ctrl + 3 sẽ chạy chương trình EXE
B4 Chẩn lỗi chương trình : nhấn Ctrl + 4 sẽ debug chương trình EXE
MOV des,src : chép dữ liệu từ src sang des
INC des : tăng des một đơn vị
DEC des : giảm des một đơn vị
ADD des,src : des = des + src
SUB des,src : des = des – src
INT num : gọi ngắt
Tài liệu tham khảo
1 Nguyễn Minh Tuấn, Giáo trình hợp ngữ - Chương 1, ĐHKHTN, 2002
2 Randal Hyde, The art of assembly language programming – Chapter 1
Ví dụ:
De chay duoc 1 CT hop ngu ban can thuc hien cac buoc sau:
Dich file ASM thanh file OBJ
Trang 4Lien ket file OBJ thanh file EXE Chay file EXE
Bài 3 Viết CT nhập vào 1 ký tự, xuất ra ký tự liền trước và liền sau
Ví dụ:
Moi ban nhap 1 ky tu: b
Ky tu lien truoc: a
Ky tu lien sau: c Bài 4 Viết CT nhập vào 1 ký tự thường In ra ký tự Hoa
Mở rộng
1 Tự tìm hiểu xem hàm nào trong ngắt 21h dùng để nhập một xâu kí tự ? Ngoài ngắt 21h, còn ngắt nào có thể dùng để nhập xuất từ bàn phím ? (dùng NortonGuide hoặc TechHelp)
2 Viết chương trình nhập tên và in ra màn hình câu “Hello ” + tên đã nhập
3 Tìm hiểu xem tại sao không có lệnh MOV x1, x2 (x1,x2 là hai biến trong bộ nhớ)
4 Hai lệnh “INC AX” và “ADD AX, 1” khác nhau chỗ nào ?
Hướng dẫn
Bài 1 Để nhập 1 một ký tự sử dụng hàm 1 của ngắt 21h, để xuất, sử dụng hàm 2
Ví dụ:
mov AH,1
int 21h ; kết quả trong AL
mov DL,AL ; kí tự cần xuất trong DL
mov AH,2
int 21h
Bài 2 Cặp kí tự xuống dòng là 10,13 Có thể khai báo nhiều xâu kí tự hoặc chung một xâu
Ví dụ:
Msg3 DB 10,13,9,“1 Dich file ASM thanh file OBJ.$”
Msg4 DB 10,13,9,“2 Lien ket file OBJ thanh file EXE.$” Hoặc
Trang 5Msg34 DB 10,13,9,“1 Dich file ASM thanh file OBJ.”
DB 10,13,9,“2 Lien ket file OBJ thanh file EXE.$”
Bài 3, 4 Kí tự hoa và kí tự thường của cùng một chữ cái tiếng Anh cách nhau 20h Do đó, để
chuyển đổi chữ hoa thành chữ thường và ngược lại, chỉ cần dùng lệnh ADD, SUB
Bài 5 Để chuyển đổi các kí tự „0‟ – „9‟ thành số 0 – 9 chỉ cần thực hiện phép trừ đi 48 (mã của „0‟)
Sau khi thực hiện phép tính, chuyển đổi thành kí tự và in ra màn hình (có thể dùng biểu diễn Hex)
Trang 6Bài thực hành số 2
Lệnh so sánh – Lệnh nhảy – Lệnh lặp
Mục đích
Hiểu cách so sánh hai số trong hợp ngữ
Hiểu cách thay đổi thứ tự thực hiện các lệnh
Biết cách sử dụng các lệnh so sánh, nhảy và lặp
Tóm tắt lý thuyết
Lệnh so sánh
Trong hợp ngữ, muốn so sánh hai số, ta phải thực hiện một phép toán số học hoặc logic trên hai
số đó và căn cứ vào các bit trong thanh ghi cờ rồi đưa ra kết luận Để làm việc này, có thể dùng lệnh CMP và TEST
Bản chất của lệnh CMP Des,Src là lệnh SUB Des,Src (thực hiện phép tính Des – Src) nhưng
kết quả của phép tính không được lưu vào Des như trong lệnh SUB
Ví dụ: so sánh hai số nguyên dương
MOV AH,1
MOV AL,2
CMP AH,AL
Sau khi thực hiện hai lệnh trên, cờ Carry (CF) bật, báo hiệu rằng AH < AL
Bản chất của lệnh TEST Des,Src là lệnh AND Des,Src (thực hiện phép tính Des AND Src)
nhưng kết quả của phép tính không được lưu vào Des như trong lệnh AND
Ví dụ: kiểm tra hai bit cuối cùng của AL
Lệnh nhảy không điều kiện
JMP <target>
Có các trường hợp sau:
JMP SHORT <tên nhãn> (short jump) Khi đó trong mã lệnh lưu 1 byte khoảng
cách (offset) giữa vị trí hiện tại và vị trí cần nhảy đến Kiểu này chỉ nhảy trong phạm
vi từ –128 đến +127 byte so với vị trí hiện tại
Ví dụ: JMP SHORT Calculate
JMP <tên nhãn> (near jump) Khi đó trong mã lệnh lưu 2 byte khoảng cách
Trang 7(offset) giữa vị trí hiện tại và vị trí cần nhảy đến Kiểu này nhảy tùy ý trong phạm vi segment
Ví dụ: JMP Calculate
JMP FAR PTR <tên nhãn> (far jump) Khi đó trong mã lệnh lưu offset và segment của vị trí cần nhảy đến Kiểu này nhảy đến bất kì chỗ nào
Ví dụ: JMP FAR PTR Calculate
JMP <con trỏ 2 byte> (near indirect jump) Khi đó trong mã lệnh lưu địa
chỉ offset của một ô nhớ Khi thực hiện, IP sẽ được gán bằng giá trị lưu tại địa chỉ này Có thể kết hợp dùng với định vị chỉ số
JMP <con trỏ 4 byte> (far indirect jump) Tương tự trường hợp trên, nhưng
con trỏ gồm cả segment và offset Chỉ khác ở khai báo con trỏ
JMP <thanh ghi 2 byte> (indirect jump via regs) Nhảy đến địa chỉ lưu
trong thanh ghi AX
Ví dụ:
MOV ax, offset Calculate
Trang 8
Lệnh nhảy có điều kiện
Đối với bộ vi xử lý 80286 trở xuống, lệnh nhảy có điều kiện có độ dài 2 byte, byte đầu tiên chứa
mã lệnh, byte thứ hai chứa khoảng cách tương đối từ lệnh đến nhãn, vì vậy <Label> trong lệnh nhảy
có điều kiện phải nằm trong khoảng từ -128 đến 127 so với vị trí lệnh nhảy Muốn nhảy xa hơn ta phải dùng kết hợp lệnh nhảy không điều kiện JMP
Từ 80386 trở lên, bộ lệnh được bổ sung, cho phép sử dụng lệnh nhảy có điều kiện có độ dài 4 byte, do đó <Label> có quyền nằm tùy ý trong cùng phạm vi segment
Khi sử dụng lệnh nhảy có điều kiện sau khi thực hiện phép so sánh, phải đặc biệt lưu ý toán hạng trong phép so sánh là số có dấu (signed) hay không có dấu (unsigned) để lựa chọn lệnh cho phù hợp
Một số lệnh nhảy có điều kiện thường dùng (tham khảo thêm trong SGK trang 81):
JE, JZ (nhảy nếu bằng)
JA (nhảy nếu lớn hơn, không dấu), JG (nhảy nếu lớn hơn, có dấu), JB (nhảy nếu nhỏ hơn, không dấu), JL (nhảy nếu nhỏ hơn, có dấu)
JAE (nhảy nếu lớn hơn hay bằng, không dấu), JGE (nhảy nếu lớn hơn hay bằng, có dấu), JBE (nhảy nếu nhỏ hơn hay bằng, không dấu), JLE (nhảy nếu nhỏ hơn hay
bằng, có dấu)
JNE, JNZ (nhảy nếu không bằng)
Ví dụ: nếu AL là số nguyên không dấu thì đoạn chương trình ở trên phải sửa lại như sau:
MOV AH,AL CMP AH,1 JAE Greater
Greater:
Lệnh lặp
Bằng cách dùng các lệnh nhảy có thể tạo ra vòng lặp Tuy nhiên, để viết chương trình tiện lợi và ngắn gọn, có thể dùng thêm các lệnh lặp như LOOP, LOOPZ,…
Lệnh LOOP <Label> tự động giảm CX một đơn vị, sau đó kiểm tra xem CX có bằng 0, nếu
không bằng thì nhảy đến nhãn <Label>
Lệnh LOOPZ <Label> tự động giảm CX một đơn vị, sau đó kiểm tra xem CX có bằng 0
hoặc cờ ZF có bật không, nếu cả hai điều này không xảy ra thì nhảy đến nhãn <Label>
Trang 9Tài liệu tham khảo
1 Nguyễn Minh Tuấn, Giáo trình hợp ngữ - Chương 4, ĐHKHTN, 2002
2 Randal Hyde, The art of assembly language programming – Chapter 6, 10
3 Dan Rollins, TechHelp v.6.0
Ví dụ: N = 3, M = 5 => USCLN(3, 5) = 1 => 3, 5 là 2 số nguyên tố cùng nhau
Bài 5 Dùng lệnh lặp, viết chương trình nhập vào 1 chuỗi ký tự Sau khi nhập xong đếm xem chuỗi
có bao nhiêu ký tự Xuất số ký tự có trong chuỗi
Ví dụ: S = "Hello world !" ==> Số kí tự trong chuỗi là 13
Bài 6 Nhập vào 2 chuỗi số, đổi 2 chuỗi thành số, sau đó cộng hai số, đổi ra chuỗi và xuất chuỗi tổng
Ví dụ: S1 = "123" => N1 = 123
S2 = "456" => N2 = 456
N = N1 + N2 = 123 + 456 = 579 => S = "579" (xuất S ra màn hình)
Trang 10Bài 7 Viết chương trình cho phép nhập vào một chuỗi S
Đổi tất cả ký tự thường thành ký tự hoa
Đổi tất cả ký tự hoa thành ký tự thường
Bài 8 Nhập và xuất mảng 1 chiều Tìm phần tử max, min, tính tổng các phần tử trong mảng
Ví dụ: N = 5
A[N] = {3,1,2,7,4}
=> max = 7, min = 1, tổng = 17
Bài 9 Cài đặt thuật toán Bubble Sort dùng ASM
Thuật toán Bubble Sort theo ngôn ngữ C như sau:
for (int i = 0; i< N-1; i++)
for(int j=N-1;j > i; j )
if(a[j] < a[j-1])
Hoan_Vi (a[j], a[j-1]);
Bài 10 Nhập và xuất mảng A hai chiều
a Tính tổng các phần tử trên đường chéo chính, đường chéo phụ
b Đếm số phần tử 0 và phần tử khác 0 trong mảng
c Tìm phần tử max của mỗi dòng, mỗi cột Tính tổng của mỗi dòng, mỗi cột
d Nhập 1 mảng hai chiều B, tạo một mảng hai chiều C có các phần tử trên dòng chẵn bằng với các phần tử trên dòng chẵn của A, các phần tử trên dòng lẻ bằng các phần tử trên dòng lẻ của B
Mở rộng
1 Trong bài tập 5, làm sao để đếm số từ có trong chuỗi kí tự?
2 Trong bài tập 10, làm sao để thể hiện một menu cho phép người dùng chọn trong các kí tự từ
„a‟ đến „d‟ sau đó thực hiện công việc ứng với chữ cái đó
CBS DB "CHAO BUOI SANG$"
CBT DB "CHAO BUOI TRUA$"
CBC DB "CHAO BUOI CHIEU$"
Trang 11Bài 3 Để nhập một số nguyên, có thể làm như sau: đầu tiên nhập xâu kí tự chứa các số từ 0 đến 9,
sau đó đổi từng kí tự ra số và nhân với các lũy thừa tương ứng của 10 và cộng lại
Bài 9 Xem ví dụ sau: Lặp gồm 2 vòng lặp (xếp mảng A có N phần tử tăng dần)
MOV N, 10 ;giả sử mảng A gồm N ký tự, trong ví dụ này N=10
FOR_J:
CMP DL, A[DI]
JB LAP MOV BL, A[DI]
MOV A[DI], DL MOV A[SI], BL MOV DL, A[SI]
LAP:
INC DI LOOP FOR_J INC SI POP CX LOOP FOR_I
Trang 12Bài thực hành số 3
Ngăn xếp – Thủ tục – Macro
Mục đích
Hiểu được cơ chế hoạt động của ngăn xếp, quá trình gọi một thủ tục
Biết cách sử dụng ngăn xếp, khai báo và gọi thủ tục
Biết cách tạo và sử dụng macro
Tóm tắt lý thuyết
Ngăn xếp
1 Một số lưu ý:
Ngăn xếp (Stack) là vùng nhớ đặc biệt được truy cập theo cơ chế “vào trước ra sau”
(LIFO – Last In First Out), nghĩa là dữ liệu nào đưa vào sau sẽ được lấy ra trước
Ngăn xếp gồm nhiều phần tử, mỗi phần tử là một từ (2 bytes)
Vị trí của ngăn xếp trong bộ nhớ được xác định bởi cặp thanh ghi SS:SP (SS chứa địa chỉ đoạn, SP chứa địa chỉ ô của đỉnh ngăn xếp) Khi chưa sử dụng, ngăn xếp rỗng, vị trí được xác định bởi SP lúc đó là đáy ngăn xếp
2 Khai báo:
.STACK <kích thước của ngăn xếp>
Ví dụ: khai báo một vùng ngăn xếp có kích thước 256 bytes:
.STACK 100h
3 Các thao tác:
Đưa trị vào (đỉnh) ngăn xếp:
PUSH <nguồn> ; đưa nguồn (thanh ghi hay từ nhớ
; 16 bit) vào đỉnh ngăn xếp
PUSHW <hằng> ; đưa trực tiếp một hằng16 bit vào
; đỉnh ngăn xếp
PUSHF ; đưa nội dung thanh ghi cờ vào đỉnh ; ngăn xếp
Lấy trị (ở đỉnh) ra khỏi ngăn xếp:
POP <đích> ; lấy giá trị (2 bytes) ở đỉnh ngăn xếp
; đưa vào đích (thanh ghi (trừ thanh
; ghi IP) hay từ nhớ 16 bit)
POPF ; lấy giá trị (2 bytes) ở đỉnh ngăn xếp
; đưa vào thanh ghi cờ Chú ý : Các lệnh PUSH, PUSHF, POP và POPF không ảnh hưởng tới các cờ
Ví dụ: Chương trình xuất chuỗi ngược dùng stack:
Trang 13Nhập ký tự „c‟:
Xuất ký tự „a‟:
…
0FCh 0FCh SS:SP
000h
…
0FCh 0FCh SS:SP
…
0FCh 0FCh SS:SP
000h
100h
…
0FCh 0FCh SS:SP
000h
100h
…
0FCh 0FCh
Trang 143 Hoạt động của lời gọi thủ tục:
Khi thực hiện lời gọi thủ tục (CALL) thì:
Địa chỉ ô của lệnh kế lệnh CALL (*) sẽ được cất vào ngăn xếp
Địa chỉ ô của lệnh đầu tiên trong thủ tục được đưa vào IP
Khi thực hiện lệnh RET để quay về trình gọi thì:
Địa chỉ trong ngăn xếp được lấy ra và được vào IP
Do đó, nếu trong thủ tục có thao tác với ngăn xếp thì trong thủ tục, trước khi thao tác với ngăn xếp ta nên lưu lại địa chỉ (*) ở trên (chính là giá trị hiện thời trong ngăn xếp) để quay trở về trình gọi Xem mô tả trong ví dụ sau
Ví dụ: Nhập xuất chuỗi kí tự
Trang 155 Trả lại địa chỉ quay về
BX = địa chỉ lệnh “CALL Xuat”
000h
…
0FCh 0FCh 100h SS:SP
…
0FCh 0FCh SS:SP
…
0FCh 0FCh SS:SP
000h
…
0FCh 100h 0FCh SS:SP
Trang 16Macro
1 Một số lưu ý:
Khi chúng ta có nhiều đoạn code giống nhau, chúng ta có thể sử dụng macro để thay thế, giống như chúng ta dùng define ở trong C
Bản chất là thay thế lời gọi macro bằng các lệnh trong thân macro
Các macro nên phục hồi những thanh ghi mà nó sử dụng trừ những thanh ghi chứa
Tạo macro trực tiếp trong chươnng trình:
Các macro thường được khai báo ở đầu chương trình trước phần code
Ví dụ: Xuất một chuỗi ra màn hình sử dụng macro
.model small stack 100h data
chuoi1 db “hello”,10,13,‟$‟
chuoi2 db “bye”,10,13,‟$‟
@xuatchuoi macro chuoi
lea dx,chuoi mov ah,9 int 21h endm
Xây dựng thư viện các macro:
Tạo 1 thư viện (tập tin) chứa các macro
include vào chương trình (thường trước phần code) bằng lệnh include
Ví dụ: Xuất một chuỗi ra màn hình sử dụng thư viện macro
THUVIEN.INC
@xuatchuoi macro chuoi
lea dx,chuoi mov ah,9 int 21h endm
Trang 17TestMacro.asm
.model small stack 100h data
4 Các thành phần cục bộ của macro:
Trong macro, ta cũng có thể khai báo các biến, nhãn cục bộ để tránh gây ra lỗi khi
gọi macro nhiều lần
Cú pháp :
LOCAL <danh sách các nhãn, các biến cục bộ>
Ví dụ: Xuất một chuỗi hằng ra màn hình sử dụng macro với biến cục bộ
.model small stack 100h
@xuatchuoi macro chuoi LOCAL chuoicucbo, nhancucbo .data
chuoicucbo db chuoi,‟$‟
.code
lea dx,chuoicucbo mov ah,9
int 21h endm
.code
… nhancucbo:
Trang 18Tài liệu tham khảo
1 Nguyễn Minh Tuấn, Giáo trình hợp ngữ - Chương 6, ĐHKHTN, 2002
2 Randal Hyde, The art of assembly language programming – Chapter 11,12
Bài 2: Tính giá trị biểu thức đã nhập ở bài tập 2 theo thứ tự từ trái sang phải
Bài 3: Viết lại các bài tập tuần trước dưới dạng các thủ tục
Bài 4: Xây dựng một thư viện các macro