Tải bản đầy đủ (.pdf) (26 trang)

môn kiến trúc máy tính hợp ngữ assembly language

Bạn đang xem bản rút gọn của tài liệu. Xem và tải ngay bản đầy đủ của tài liệu tại đây (827.22 KB, 26 trang )

<span class="text_page_counter">Trang 1</span><div class="page_container" data-page="1">

HỌC VIỆN HÀNG KHÔNG VIỆT NAMKHOA CƠNG NGHỆ THƠNG TIN

MƠN: KIẾN TRÚC MÁY TÍNH

Giảng viên phụ trách: Nguyễn Hữu HiệpSinh viên : Ngô Anh Kiệt - 2331540206

Đoàn Gia Bảo Ngọc – 2331540045Trần Hoàng Nhân – 2331540023Đoàn Nguyễn Nhã Quỳnh – 2331540282Nguyễn Trọng Ngọc Sơn – 2331540223Phạm Thị Hương Giang – 2331540211Nguyễn Phi Hùng – 2331540080Trần Thị Thúy – 2331540009

Tp HCM, ngày 22 tháng 01 năm 2024

<small>1</small>

</div><span class="text_page_counter">Trang 2</span><div class="page_container" data-page="2">

LỜI MỞ ĐẦU

Hợp ngữ (Assembly language) là một ngơn ngữ lập trình cấp thấp, sử dụng các mã máy để điều khiển máy tính. Hợp ngữ có thể được sử dụng để viết các chương trình máy tính hiệu quả và tối ưu, nhưng nó cũng có thể khó học và sử dụng.

Hợp ngữ được tạo thành từ các lệnh máy, là các mã nhị phân có thể được máy tính hiểu và thực thi. Mỗi lệnh máy tương ứng với một hành động cụ thể mà máy tính sẽ thực hiện. Ví dụ, lệnh máy ADD sẽ cộng hai số lại với nhau, lệnh máy MOV sẽ di chuyển dữ liệu từ một vị trí bộ nhớ sang vị trí khác, và lệnh máy JMP sẽ chuyển hướng thực thi đến một vị trí trong chương trình.

Hợp ngữ thường được sử dụng để viết các chương trình hệ thống, chẳng hạn như trình điều khiển thiết bị và hệ điều hành. Các chương trình này thường yêu cầu hiệu suất tối đa và cần kiểm soát chặt chẽ đối với phần cứng máy tính.

Hợp ngữ cũng có thể được sử dụng để viết các chương trình ứng dụng, chẳng hạn như trò chơi và ứng dụng phần mềm. Tuy nhiên, hợp ngữ thường không được sử dụng cho các ứng dụng này vì nó khó học vàsử dụng hơn các ngơn ngữ lập trình cấp cao

<small>2</small>

</div><span class="text_page_counter">Trang 3</span><div class="page_container" data-page="3">

M C L C:Ụ Ụ

I.Cú pháp của chương trình hợp ngữ...4

II.Dữ liệu cho chương trình hợp ngữ:...5

<small>Dữ liệu nguyên thủy:...6</small>

<small>Dữ liệu cấu trúc:...6</small>

<small>Dữ liệu số:...7</small>

<small>Dữ liệu ký tự:...7</small>

III.Tập lệnh hợp ngữ(Nhóm lệnh chuyển dữ liệu):...8

<small>1.Nhóm lệnh chuyển dữ liệu đa dụng:...8</small>

<small>3.Nhóm lệnh chuyển cờ hiệu (thanh ghi trạng thái):...14</small>

<small>Lệnh PUSHF (push flag):...14</small>

<small>Lệnh POPF (pop flag):...14</small>

<small>Lệnh SAHF (Store AH into Flags):...15</small>

<small>Lệnh LAHF (Load AH from Flags):...16</small>

<small>4.Nhóm lệnh chuyển địa chỉ:...18</small>

<small>Lệnh LEA (load Efective address):...18</small>

<small>Lệnh LDS: (Load pointer using DS):...19</small>

<small>Lệnh LES (Load register and ES with words from memory):...20</small>

</div><span class="text_page_counter">Trang 4</span><div class="page_container" data-page="4">

I.Cú pháp của chương trình hợp ngữ

Trong chương trình hợp ngữ, mỗi lệnh được đặt trên một dòng – dòng lệnh;lệnh có 2 dạng:

Lệnh thật: là các lệnh gợi nhớ của vi xử lýVD: MOV, SUB, ADD, ….

Khi dịch, lệnh gợi nhớ được dịch ra mã máyLệnh giả: là các hướng dẫn chương trình dịch

VD: MAIN PROC, .DATA, END MAIN, …

Khi dịch, lệnh giả không được dịch ra mã máy mã chỉ có tác dụng định hướng cho chương trình dịch.

Khơng phân biệt chữ hoa hay chữ thường trong các dòng lệnh hợp ngữ khi được dịch.

Cấu trúc dòng lệnh hợp ngữ:

Một câu lệnh hợp ngữ thường bao gồm các thành phần sau:

liệu từ một vị trí bộ nhớ sang vị trí khác.

hoặc hai địa chỉ bộ nhớ cần di chuyển dữ liệu.

[Tên] [mã lệnh] [các toán hạng] [chú giải]

<small>5</small>

</div><span class="text_page_counter">Trang 5</span><div class="page_container" data-page="5">

START: MOV AH,100 ; chuyển 100 vào thanh ghi AHCác trường của dịng lệnh:

- Số lượng tốn hạng phụ thuộc vào lệnh cụ thể- Có thể có 0, 1 và 2 tốn hạng

Chú giải:

- Là chú thích cho dịng lệnh- Bắt đầu bằng dấu chấm phẩy (;)

II.Dữ liệu cho chương trình hợp ngữ:

Dữ liệu trong chương trình hợp ngữ được lưu trữ trong bộ nhớ máy tính. Dữ liệu có thể được chia thành hai loại:

-Dữ liệu nguyên thủy: Dữ liệu nguyên thủy là dữ liệu không được cấu trúc, chẳng hạn như số, ký tự và boolean.

<small>6</small>

</div><span class="text_page_counter">Trang 6</span><div class="page_container" data-page="6">

-Dữ liệu cấu trúc: Dữ liệu cấu trúc là dữ liệu được cấu trúc thành các phần, chẳng hạn như mảng, cấu trúc và tập hợp.

Dữ liệu nguyên thủy:

Dữ liệu nguyên thủy được lưu trữ trong bộ nhớ dưới dạng các giá trị nhị phân. Kiểu dữ liệu của dữ liệu nguyên thủy xác định số lượng bit cần thiết để lưu trữ giá trị.

Các kiểu dữ liệu nguyên thủy phổ biến trong hợp ngữ bao gồm:· BYTE: Kiểu dữ liệu 8 bit

· WORD: Kiểu dữ liệu 16 bit· DWORD: Kiểu dữ liệu 32 bit· QWORD: Kiểu dữ liệu 64 bit

Ví dụ, biến a sau là kiểu dữ liệu 8 bit và có thể lưu trữ một giá trị từ 0 đến 255:VAR a:BYTE

Dữ liệu cấu trúc:

Dữ liệu cấu trúc là dữ liệu được cấu trúc thành các phần. Mỗi phần của dữ liệu cấu trúc được gọi là thành phần.

Các kiểu dữ liệu cấu trúc phổ biến trong hợp ngữ bao gồm:

-Mảng: Mảng là một tập hợp các giá trị có cùng kiểu dữ liệu. Các giá trị trong mảng được lưu trữ liên tiếp trong bộ nhớ.

-Cấu trúc: Cấu trúc là một kiểu dữ liệu được tạo thành từ nhiều thành phần. Mỗi thành phần của cấu trúc có thể có kiểu dữ liệu khác nhau.

-Tập hợp: Tập hợp là một danh sách các giá trị có thể lặp lại. Các giá trị trong tập hợp được lưu trữ theo thứ tự khơng rõ ràng.

Ví dụ, mảng a sau có 10 phần, mỗi phần là một số nguyên:VAR a [10]: BYTE

<small>7</small>

</div><span class="text_page_counter">Trang 7</span><div class="page_container" data-page="7">

Cấu trúc b sau có hai thành phần, mỗi thành phần là một số nguyên:TYPE

X:BYTEy:BYTEENDENDTYPEVAR b:struct_t

Tập hợp c sau có 5 phần, mỗi phần là một chữ cái:VAR c: SET OF CHAR

Dữ liệu số:

Thập phân: 0-9Thập lục phân: 0-9, A-F

Bắt đầu bằng 1 chữ (A-F) thì thêm 0 vào đầuThêm ký hiệu H (Hexa) ở cuối

VD: 80H, 0F9HNhị phân: 0-1

Thêm ký hiệu B (binary) ở cuốiVD: 0111B, 1000B

Dữ liệu ký tự:

Bao trong cặp nháy đơn hoặc képCó thể dùng ở dạng ký tự hoặc mã ASCII

<small>8</small>

</div><span class="text_page_counter">Trang 8</span><div class="page_container" data-page="8">

‘A’=65, ‘a’=97

III.Tập lệnh hợp ngữ(

Nhóm lệnh chuyển dữ liệu):

1.Nhóm lệnh chuyển dữ liệu đa dụng:Lệnh MOV:

-Lệnh MOV trong hợp ngữ là lệnh di chuyển dữ liệu từ một vị trí bộ nhớ sang một vị trí bộ nhớ khác.

liệu khác nhau. Trong trường hợp này, bộ xử lý sẽ tự động chuyển đổi kiểu dữ liệu của dữ liệu. Ví dụ, lệnh MOV sau sẽ di chuyển giá trị của biến a, là một số nguyên 8 bit, sang biến b, là một số nguyên 16 bit:

VAR a:BYTEVAR b:WORD

</div><span class="text_page_counter">Trang 9</span><div class="page_container" data-page="9">

Lệnh PUSH:

-Là một lệnh thường được sử dụng để lưu trữ dữ liệu vào ngăn xếp. Ngăn xếp là một cấu trúc dữ liệu LIFO (Last In First Out), nghĩa là dữ liệu được lưu trữ vào ngăn xếp theo thứ tự ngược lại với thứ tự lấy ra.

operand là dữ liệu cần lưu trữ vào ngăn xếp. Có thể là một thanh ghi, một giá trị số hoặc một địa chỉ bộ nhớ.

Hoạt động:

- Lệnh push sẽ đẩy operand lên đỉnh ngăn xếp.

- Ngăn xếp là một cấu trúc dữ liệu nằm trong bộ nhớ. Khi một lệnh push được thực thi, CPU sẽ ghi operand vào vị trí đầu tiên của ngăn xếp. Sau đó, CPU sẽ tăng chỉ số ngăn xếp lên 1 byte.

Đoạn mã:

Trong ví dụ này, lệnh push sẽ lưu trữ các giá trị 10, 20 và 30 vào ngăn xếp, theo thứ tự ngược lại.

Lợi ích của lệnh push:

Lệnh push cho phép lưu trữ dữ liệu tạm thời vào ngăn xếp. Điều này có thể hữu ích cho các tình huống sau:

Lưu trữ dữ liệu cần sử dụng trong một hàm.Lưu trữ dữ liệu cần sử dụng trong một vòng lặp.Lưu trữ dữ liệu cần sử dụng trong một phép tính.<small>10</small>

</div><span class="text_page_counter">Trang 10</span><div class="page_container" data-page="10">

Lệnh push có thể được sử dụng để gọi hàm. Khi một hàm được gọi, các tham số của hàm sẽ được đẩy lên ngăn xếp. Sau khi hàm được thực thi, các tham số sẽ được lấy ra khỏi ngăn xếp.

Lệnh POP:

-Là một lệnh thường được sử dụng để lấy dữ liệu ra khỏi ngăn xếp. Ngăn xếp là một cấu trúc dữ liệu LIFO (Last In First Out), nghĩa là dữ liệu được lấy ra khỏi ngăn xếp theo thứ tự ngược lại với thứ tự lưu trữ.

Đoạn mã:

Trong ví dụ này, lệnh pop sẽ lấy giá trị 30 từ đỉnh ngăn xếp và lưu trữ vào thanh ghi AX.

Lợi ích của lệnh pop:

<small>11</small>

</div><span class="text_page_counter">Trang 11</span><div class="page_container" data-page="11">

Lệnh pop cho phép lấy dữ liệu tạm thời ra khỏi ngăn xếp. Điều này có thể hữu ích cho các tình huống sau:

Lấy dữ liệu đã được lưu trữ trong một hàm.Lấy dữ liệu đã được lưu trữ trong một vòng lặp.Lấy dữ liệu đã được lưu trữ trong một phép tính.

Lệnh pop có thể được sử dụng để trả về từ hàm. Khi một hàm trả về, dữ liệu đã được lưu trữ trên ngăn xếp bởi hàm sẽ được lấy ra khỏi ngăn xếp.

Lệnh XCHG:

-Là một lệnh trong hợp ngữ dùng để hoán đổi giá trị của hai thanh ghi. Cú pháp của lệnh XCHG như sau:

XCHG reg1, reg2-Chức năng:

Lệnh XCHG là một lệnh máy tính có chức năng hoán đổi giá trị của hai thanh ghi hoặc vùng nhớ. Chức năng cụ thể của lệnh xchg phụ thuộc vào ngữ cảnh sử dụng, nhưng chủ yếu là để hốn đổi giá trị của các ơ nhớ hoặc thanh ghi.

giá trị của hai thanh ghi hoặc một thanh ghi với một vùng nhớ. Trong ngữ cảnh củalập trình hợp ngữ, lệnh xchg có thể được sử dụng để đổi giá trị giữa hai biến hoặc hai thanh ghi.

-Ví dụ trong ngơn ngữ lập trình Assembly, lệnh xchg có thể được sử dụng như sau:xchg ax, bx ; Đổi giá trị giữa thanh ghi AX và BX

xchg [si], ax ; Đổi giá trị giữa nội dung tại địa chỉ con trỏ DS:SI và thanh ghi AXTrong các ngôn ngữ lập trình hiện đại, đặc biệt là trong các ngơn ngữ bậc cao, việc sử dụng lệnh xchg trực tiếp từ ngơn ngữ lập trình có thể khơng phổ biến. Tuy nhiên, lệnh xchg vẫn có thể được sử dụng ẩn đằng sau các phép toán gán để thực hiện việc hốn đổi giá trị của hai biến mà khơng cần sử dụng biến trung gian.-Ví dụ trong một ngơn ngữ như C:

int a = 5;

<small>12</small>

</div><span class="text_page_counter">Trang 12</span><div class="page_container" data-page="12">

int b = 10;

// Sử dụng lệnh xchg ẩn đằng sau phép toán gán để hoán đổi giá trị của a và b a = a ^ b;

b = a ^ b; a = a ^ b;

Trong trường hợp này, các phép toán XOR được sử dụng để thực hiện việc hoán đổi giá trị của hai biến mà không cần một biến tạm thời.

Lệnh XLAT:

chuyển nội dung của ô nhớ 8 bit vào thanh ghi AL. Địa chỉ ô nhớ xác định bằng cặp thanh ghi DS:BX (nếu không chỉ ra src) hay src, địa chỉ offset chứa trong thanh ghi AL.

Cú pháp: XLAT [src]

Lệnh XLAT tương đương với các lệnh: MOV AH,0

MOV SI,AX MOV AL,[BX+SI]Đoạn mã

<small>13</small>

</div><span class="text_page_counter">Trang 13</span><div class="page_container" data-page="13">

- Ví dụ : bài tốn tính bình phương một số nguyên có thể thực hiện bằng cách tra bảng như sau

MOV CX,1000h MOV DS,CX

MOV BX, ,2000h ; địa chỉ đầu bảng

- Sau khi làm xong lệnh XLAT : AL = 25= 52

2.

Nhóm lệnh chuyển dữ liệu qua cổng:

Mỗi I/O port giao tiếp với CPU sẽ có một địa chỉ 16 bit cho nó. CPU gởi hay nhận dữ liệu từ cổng bằng cách chỉ đến địa chỉ cổng đó. Tuỳ theo chức năng mà cổng có thể: chỉ đọc dữ liệu (input port), chỉ ghi dữ liệu (output port) hay có thể đọc và ghi

IN AL, 1fh ;nội dung cổng 1fh đưa vào AL

Nếu địa chỉ cổng >= 256 thì phải nhờ đến thanh ghi DXVí dụ: địa chỉ COMI = 378h

mov dx, 378hin al,dxLệnh OUT:

Chức năng: đưa dữ liệu từ thanh ghi AL/AX ra cổngCú pháp: OUT địa chỉ cổng ,AL/AX

Chú ý:

Nếu địa chỉ cổng <256 thì số địa chỉ đứng trực tiếp trong lệnh OUTVí dụ: địa chỉ cổng là 1fh

OUT 1fh,AL ;nội dung cổng 1fh đưa vào AL

Nếu địa chỉ cổng >=256 thì phải nhờ đến thanh ghi DX

<small>14</small>

</div><span class="text_page_counter">Trang 14</span><div class="page_container" data-page="14">

Ví dụ: địa chỉ COMI = 378hmov dx,378h

out dx,al

lệnh này khơng tác động đén cờ

3.Nhóm lệnh chuyển cờ hiệu (thanh ghi trạng thái):

Lệnh PUSHF (push flag): Lưu giữ thanh ghi cờ vào ngăn xếp.

Cú pháp: pushf

Hoạt động:

Lệnh pushf sao chép toàn bộ giá trị của thanh ghi cờ (flags register) gồm 16 cờ khác nhau, ví dụ như cờ carry (CF), cờ zero (ZF), cờ overflow (OF), cờ parity (PF), etc.

Giá trị sao chép này được đẩy lên vị trí đầu tiên của ngăn xếp (SP).Chỉ số ngăn xếp (SP) được tăng lên 2 byte để trỏ đến vị trí trống tiếp theo trên ngăn xếp.

Lợi ích của lệnh pushf:

Lưu trữ tạm thời giá trị của thanh ghi cờ để có thể phục hồi trạng thái cờ sau này. Điều này thường được sử dụng trong các tình huống sau:

Khi xử lý ngắt (interrupt), giá trị cờ trước khi ngắt được lưu trữ bằng pushf để có thể phục hồi sau khi xử lý xong ngắt.

Khi thực hiện một đoạn mã phức tạp có thể thay đổi nhiều cờ, có thể pushf trước khi bắt đầu để có thể quay lại trạng thái cờ ban đầu nếu cần.Lưu ý: Lệnh pushf chỉ đẩy giá trị cờ, không đẩy bất kỳ dữ liệu nào khác.Ví dụ: để đưa nội dung của thanh ghi cờ vào thanh ghi AX ta làm như sau:PUSHF

POP AX

Đây là lệnh lấy 2 byte từ ngăn xếp đưa vào thanh ghi cờ.

</div><span class="text_page_counter">Trang 15</span><div class="page_container" data-page="15">

Chỉ số ngăn xếp (SP) được giảm xuống 2 byte để trỏ đến vị trí trống tiếp theo trên ngăn xếp.

Lợi ích của lệnh popf:

Phục hồi trạng thái cờ sau khi đã được lưu trữ bằng lệnh pushf. Điều này thường được sử dụng trong các tình huống sau:

Sau khi xử lý ngắt (interrupt), giá trị cờ trước khi ngắt được phục hồi bằng popf.

Sau khi thực hiện một đoạn mã phức tạp có thể thay đổi nhiều cờ, có thể popf để quay lại trạng thái cờ ban đầu.

Lưu ý: Lệnh popf chỉ lấy giá trị cờ, không lấy bất kỳ dữ liệu nào khác.

Lệnh SAHF (Store AH into Flags):

Là một lệnh x86 assembly language được sử dụng để di chuyển giá trị từ thanh ghi AH (High byte của thanh ghi AX) vào thanh ghi cờ (Flags register). Lệnh này thường được sử dụng trong quá trình chuyển đổi giữa dạng số thập phân và dạng ASCII (ví dụ: chuyển đổi từ ký tự sang số).

Cú pháp của lệnh SAHF là: SAHF

Lệnh này khơng có tham số và khơng yêu cầu bất kỳ thanh ghi nào khác. Sau khi thực hiện lệnh SAHF, các bit trong thanh ghi cờ sẽ được cập nhật dựa trên giá trị trong thanh ghi AH.

Chức năng:

-Trong x86 assembly language được sử dụng để sao lưu giá trị của thanh ghi AH (High byte của thanh ghi AX) vào thanh ghi cờ (Flags register). Cụ thể, nó sao lưu các bit 0-7 của thanh ghi AH vào các cờ trong thanh ghi cờ.

-Thanh ghi cờ (Flags register) là một thanh ghi 16-bit trong CPU chứa các cờ trạngthái, mỗi cờ biểu thị một điều kiện sau khi thực hiện một lệnh. Các cờ chính bao gồm:

-Zero Flag (ZF): Được đặt thành 1 nếu kết quả của một phép toán là 0.

-Sign Flag (SF): Được đặt thành bít cao nhất của kết quả của một phép tốn (1 nếu âm, 0 nếu dương hoặc khơng âm).

<small>16</small>

</div><span class="text_page_counter">Trang 16</span><div class="page_container" data-page="16">

-Overflow Flag (OF): Được đặt thành 1 nếu có tràn (overflow) khi thực hiện một phép toán dấu (signed).

-Parity Flag (PF): Được đặt thành 1 nếu số lượng bit 1 trong kết quả của một phép toán là số chẵn.

-Carry Flag (CF): Được đặt thành 1 nếu có tràn (carry) khi thực hiện một phép tốnkhơng dấu (unsigned).

Trường hợp thường gặp:

Lệnh SAHF thường được sử dụng sau các lệnh chuyển đổi giữa số và ASCII, nơi giá trị trong thanh ghi AH thường đại diện cho một ký tự số. Sau khi sửdụng lệnh SAHF, bạn có thể kiểm tra các cờ để xác định điều kiện sau khi chuyển đổi và thực hiện các hành động tương ứng.

sử dụng các lệnh như JZ (Jump if Zero), JG (Jump if Greater), JL (Jump if Less),... để kiểm tra và xử lý các trường hợp đặc biệt dựa trên giá trị của các cờ.

thập phân và dạng ASCII, đặc biệt là khi xử lý ký tự số trong lập trình assembly. Khi bạn đọc một ký tự số từ đầu vào người dùng hoặc từ một nguồn dữ liệu khác, và sau đó cần chuyển đổi ký tự đó thành giá trị số, lệnh SAHF có thể giúp xác địnhđiều kiện sau khi chuyển đổi.

Trong x86 assembly language được sử dụng để nạp giá trị của thanh ghi cờ (Flags register) vào thanh ghi AH (High byte của thanh ghi AX).

Cú pháp của lệnh LAHF là: LAHF

Lệnh này khơng có tham số và khơng u cầu bất kỳ thanh ghi nào khác. Sau khi thực hiện lệnh LAHF, giá trị của thanh ghi cờ sẽ được sao chép vào thanh ghi AH, với các bit 0-7 của thanh ghi AH giống như các bit 0-7 của thanh ghi cờ.

Lệnh LAHF thường được sử dụng để đọc các cờ trạng thái sau khi thực hiện một phép tốn và lưu trữ chúng để sau đó kiểm tra và xử lý các điều kiện tương ứng.

<small>17</small>

</div><span class="text_page_counter">Trang 17</span><div class="page_container" data-page="17">

Chức năng:

năng nạp giá trị của thanh ghi cờ (Flags register) vào thanh ghi AH (High byte của thanh ghi AX). Cụ thể, lệnh này sao chép các bit trạng thái của thanh ghi cờ vào các bit tương ứng của thanh ghi AH.

này bao gồm Zero Flag (ZF), Sign Flag (SF), Overflow Flag (OF), Parity Flag (PF), và nhiều cờ khác nữa, tùy thuộc vào loại phép tốn.

cờ để sau đó có thể kiểm tra và xử lý các điều kiện tương ứng. Điều này thường xảy ra sau khi thực hiện một chuỗi các phép toán và bạn muốn kiểm tra kết quả củachúng.

bạn cần kiểm tra và xử lý các điều kiện hoặc lỗi sau khi thực hiện một loạt các phép toán trong mã assembly x86. Cụ thể, LAHF được sử dụng để nạp giá trị của thanh ghi cờ (Flags register) vào thanh ghi AH (High byte của thanh ghi AX). Các cờ này chứa thông tin về kết quả của các phép tốn trước đó, và việc lưu trữ thơng tin này trong thanh ghi AH giúp bạn dễ dàng kiểm tra các điều kiện và thực hiện xửlý tương ứng.

Dưới đây là một số tình huống phổ biến khi LAHF có thể được sử dụng:

sau đó sử dụng LAHF để kiểm tra cờ Overflow (OF) và xử lý trường hợp tràn. lahf

jnz overflow_handler ; Nhảy nếu OF không bằng 0 ; Tiếp tục xử lý nếu OF bằng 0

overflow_handler: ; Xử lý khi OF bằng 1

<small>18</small>

</div>

×