Cùng viết Assembly Language giản lược bằng asmrb gem
Bài đăng này đã không được cập nhật trong 9 năm
I. Giới thiệu chung về assembly
Assembly Language
là ngôn ngữ "cổ" nhất trong lịch sự, và ngày nay nó gần như trở nên "vô hình" đối với lập trình viên những thế hệ sau này, nhờ sự ra đời của vô số ngôn ngữ hiện đại dựa trên các máy ảo [VM - virtual machine
] và trình biên dịch/thông dịch [compiler/interpreter
] thông minh.
Virtual Machine
Sự trừu tượng hoá các thành phần bên dưới [low-level abstraction
] khiến chúng ta không còn phải giao tiếp trực tiếp với assembly. Thay vào đó, các máy ảo/trình biên dịch sẽ đảm nhận nhiệm vụ biên dịch các ngôn ngữ bậc cao [high-level language
] xuống assembly phù hợp với các kiến trúc CPU mà ta lựa chọn như x86, 64, arm ..
Low-Level
Assembly rất gần với phần cứng bên dưới, nó thực hiện phần lớn các tác vụ thông qua các lệnh [instruction
], thanh ghi CPU [Register: EAX, EBX...
] và bộ nhớ động RAM [Random Access Memory
]. Do đặc thù như vậy, nên nó thường được dùng để viết những chức năng cấp dưới, và thiên về phần cứng nhiều hơn là ứng dụng thực sự bên trên.
Stack-based Evaluation
Một cách đơn giản thì assembly có cách thực thi chương trình dựa trên bộ nhớ kiểu ngăn xếp [Stack
] giống với các ngôn ngữ hậu thứ tự [PostFix language
]:
Thay vì viết: 1 + 2
Ta viết : 1 2 +
Theo kiểu viết của assembly, nó sẽ thực hiện theo trình tự:
push value 1 on stack
push value 2 on stack
call function :add
=> result is 3
Đó là cách ngôn ngữ này thực hiện phần lớn các tác vụ.
Ngoài ra, để rẽ nhánh [branching
] và tạo vòng lặp [routine
], asmrb
sẽ nhảy [jump
] đến các nhãn [label
] được đánh dấu trong thân chương trình. Tính năng này tương tự với goto
ở Basic hay C#.
II. Asmrb - code assembly giản lược trên Ruby
Mình xin giới thiệu gem asmrb
của Ruby, một tiện ích giúp bạn làm quen với assembly.
** Cài Đặt **
> gem install asmrb
** Source code **
https://github.com/thetrung/asmrb
** Bắt Đầu **
khai báo sử dụng gem asmrb:
require "asmrb"
Tạo một block khi khởi tạo object của Asmrb:
f = Asmrb.new do
....
end
Ok, giờ là code của asmrb. Hãy khai báo 1 hàm mới với fun
và tên hàm ở dạng symbol
:
f = Asmrb.new do
fun :factorial
ret
end
một hàm trong asmrb hay assembly, thường kết thúc bằng ret
. Ngoài việc kết thúc chương trình, nó còn trả về giá trị trên cùng [còn gọi là top-most
] của stack
.
một hàm thường có các tham số [parameter
], ta khai báo bằng lệnh arg :acc, :n
.
f = Asmrb.new do
fun :factorial
arg :acc, :n
ret
end
Để sử dụng tham số hay giá trị, chúng cần được đẩy lên stack
bằng psh
:
f = Asmrb.new do
fun :demo
psh 1
psh 2
end
f.invoke
=> [1, 2] > demo
Và đẩy đối tượng top-most
ra khỏi stack bằng pop
:
pop
dbg
end
f.invoke
=> [1, 2] > demo
=> [1] > demo
Quay trở lại hàm :factorial
, ta hãy thử xem hàm này trên Ruby:
def factorial acc, n
if n < 1
acc
else
factorial acc * n, n - 1
end
end
Như vậy, cần kiểm tra điều kiện n < 1
:
true
: trả về acc
false
: gọi factorial
với 2 biểu thức acc * n
và n - 1
Biểu thức if n < 1
Theo hậu thứ tự: n 1 <
dùng psh
ta có thể viết:
psh 1
psh :n
Thêm jlt
để so sánh n < 1
và nhảy đến block :result
nếu thoả mãn điều kiện:
psh 1
psh :n
jlt :result
Code của hàm factorial
hiện tại sẽ trở thành:
f = Asmrb.new do
fun :factorial
arg :acc, :n
psh 1
psh :n
jlt :result
ret
end
* Khi điều kiện chưa thoả mãn, asmrb
sẽ tiếp tục thực thi các lệnh kế tiếp như vế else
của nó vậy.
Để tạo 1 block code, hay thực ra là tạo 1 label
để asmrb
nhảy đến, ta dùng lệnh blo
:
blo :else
ở đây, ta khởi tạo block :else
để tiếp tục phần đệ quy factorial
, với 2 biểu thức đã nói: acc * n
và n - 1
.
Trước hết ta sẽ viết 2 biểu thức này với phép nhân là mul
và phép trừ dùng sub
:
psh :n
psh :acc
mul
psh 1
psh n
sub
Và giờ gọi lại hàm factorial
bằng lệnh rec
, nói cách khác, lệnh này luôn tự nhảy về label :factorial
được tạo nhờ fun
:
rec
Lúc này, asmrb
sẽ thực thi như sau:
- Giá trị của 2 biểu thức
acc * n
vàn - 1
kia sẽ được lần lượt đẩy lên stack rec
sẽ nhảy về block:factorial
, và gán 2 giá trị trên cùng của stack cho:acc
và:n
Nhìn lại factorial
:
f = Asmrb.new do
fun :factorial
arg :acc, :n
psh 1
psh :n
jlt :result
blo :else
psh :n
psh :acc
mul
psh 1
psh n
sub
rec
ret
end
Có vẻ khá dài, chúng ta có thể rút ngắn lại bằng cách viết gộp các lệnh psh
bằng los
:
psh 1
psh 2
psh 3
# became:
los 1, 2, 3
# evaluation:
=> [1,2,3] > function
factorial
sẽ trở thành:
f = Asmrb.new do
fun :factorial
arg :acc, :n
los :n, 1
jlt :result
blo :else
los :acc, :n
mul
los :n, 1
sub
rec
ret
end
* los
sẽ tự đảo ngược thứ tự phần tử được đẩy lên stack
nên ta không cần viết ngược thứ tự như psh
.
Cuối cùng, ta viết block :result
để trả về giá trị của :acc
khi n < 1
thoả mãn:
blo :result
psh :acc
ret
end
Hàm factorial
đã hoàn thiện:
f = Asmrb.new do
fun :factorial
arg :acc, :n
los :n, 1
jlt :result
blo :else
los :acc, :n
mul
los :n, 1
sub
rec
blo :result
psh :acc
ret
end
* Khi muốn xem giá trị hiện tại của stack
, ta có thể chèn thêm dbg
vào:
f = Asmrb.new do
fun :demo
psh 1
dbg
end
f.invoke
=> [1] > demo
Để chạy hàm này, ở chế độ thông dịch qua asmrb
cùng debug cách bước chạy, thêm phần:
f.is_debug = true
result = f.invoke 1, 3
puts result
kết quả ở console:
----------------------
[1, 3] > factorial
0: arg acc n
1: los n 1
2: jlt result
3: los acc n
4: mul
5: los n 1
6: sub
7: rec
::[factorial]
0: arg acc n
1: los n 1
2: jlt result
3: los acc n
4: mul
5: los n 1
6: sub
7: rec
::[factorial]
0: arg acc n
1: los n 1
2: jlt result
3: los acc n
4: mul
5: los n 1
6: sub
7: rec
::[factorial]
0: arg acc n
1: los n 1
2: jlt result
::[result]
8: psh acc
9: ret
----------------------
6
Ở console-log
trên, ta có thể thấy:
- chương trình nhận tham số
[1, 3] > factorial
, - Sau đó chạy đến khi gặp lệnh
rec
, và nhảy về đầu chương trình::[factorial]
, - Cứ thế lặp đến khi nhảy đến block
::[result]
- Nó trả lại kết quả về
stack
và thoát chương trình.
6
Ta nhận đc kết quả phép tính sau khi invoke
là 6
.
III. Tổng quan
Trên đây chỉ là một ví dụ về cách viết một chương trình assembly đã được "giản lược" đi rất nhiều thành phần [ như Register
, Memory Pointer
, Memory Address
...] để nó trở nên đơn giản và gần gũi hơn với ngôn ngữ bậc cao.
Hy vọng các bạn sẽ tìm thấy nhiều điều thú vị với gem này. Do đang trong giai đoạn demo bản mẫu nên asmrb
chưa thật sự hoàn thiện. Mọi ý kiến / đóng góp xin đừng ngần ngại gửi về github của trên, mình xin cảm ơn (bow)
All rights reserved