# Tìm hiểu về ASL

{% hint style="danger" %}
Bài viết yêu cầu bảng đã có 1 chút nền tảng về lập trình
{% endhint %}

{% hint style="warning" %}
Bài viết này được viết dựa trên các tài liệu được công bố ở diễn đàng [ACPI Specifications](https://uefi.org/specifications)

> Do bài viết được tổng hợp từ nhiều nguồn nên có gì sai sót mong các bạn thông cảm
> {% endhint %}

## ASL là gì?

{% hint style="info" %}
Với ACPI, nó  sử dụng một ngôn ngữ độc quyền, được gọi là ASL

> Viết tắt của ACPI Source Language
>
> > Đây sẽ là phần trọng tâm của bài viết

Để tạo ra các bảng ACPI

> Các bảng này chứa thông tin về phần cứng.&#x20;

Thêm nữa sau khi được biên dịch ASL sẽ trở thành AML&#x20;

> Viết tắt của ACPI Machine Language
>
> > Và lúc này hệ thống sẽ có thể thực thi các bảng ACPI

Và cuối cùng ASL là một ngôn ngữ, nó có các quy tắc và hướng dẫn riêng.

> Phần tiếp theo sẽ nói rõ về các quy tắc và hướng dẫn này
> {% endhint %}

## ASL cơ bản

### Sơ lược về `DefinitionBlock`

{% hint style="info" %}
Nếu có 1 chút kinh nghiệm với hackintosh có lẽ cụm từ `DefinitionBlock` hoàn toàn không hễ khó bắt gặp trong các SSDT/DSDT rồi đúng không nào
{% endhint %}

{% hint style="info" %}
Ta có thể hiểu rằng `DefinitionBlock` là nền tảng của mội đoạn mã ASL&#x20;

> Cụ thể hơn là của mọi bảng ACPI
>
> > Tạm dịch: Khối định nghĩa
> >
> > > Như bài trước ta đã tìm hiểu các bảng ACPI đóng vai trò chẳng khác gì một quyển từ điển cung cấp các thông tin cho firmware

Tất cả mã ASL đều phải nằm bên trong `DefinitionBlock`&#x20;

Khối `DefinitionBlock` này được xác định bởi cặp ngoặc nhọn `{}`

> Bất kỳ mã `ASL` nào nằm bên trong cặp ngoặc nhọn này được gọi là **`Root Scope`**.
>
> > Và phạm vi giữa hai dấu ngoặc nhọn được gọi là phạm vi gốc

Thêm nữa bất kì đoạn mã ASL nào nằm ngoài khối `DefinitionBlock` đều được xem là không hợp lệ&#x20;

> Tức là không được chấp nhận bởi hệ thống ACPI
> {% endhint %}

{% hint style="success" %}
Ở đây chúng ta sẽ đến với một ví dụ
{% endhint %}

```
DefinitionBlock ("", "1234", X, "123456", "12345678", 0x00000000)

// Các ký tự trong dấu () chính là thông tin về khối DefinitionBlock
// Các con số trong "" cho biết số lượng ký tự có thể sử dụng trong mỗi phần
```

| Tham số                | Dạng dữ liệu | Mô tả                                                                                                                                                                                                |
| ---------------------- | :----------: | ---------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
| **AMLFileName**        |     `""`     | Tên của file AML được tạo ra. Bạn có thể để trống mục này, và thường thì nó được để trống                                                                                                            |
| **TableSignature**     |   `"1234"`   | Loại bảng AML, ví dụ như `DSDT` hoặc `SSDT`. Hãy dùng `4` ký tự cho mục này.                                                                                                                         |
| **ComplianceRevision** |      `X`     | Phiên bản ACPI mà bảng này sử dụng. Nếu giá trị là `1` hoặc nhỏ hơn, bảng sẽ sử dụng bản  `32 bit`. Ngược lại, giá trị từ `2` trở lên dành cho hệ thống `64 bit`. Hiện nay, giá trị mặc định là `2`. |
| **OEM ID**             |  `"123456"`  | Mã nhận diện của nhà sản xuất thiết bị gốc (`OEM`) tạo ra bảng `ACPI` này. Hãy sử dụng `6` ký tự cho mục này.                                                                                        |
| **OEM Table ID**       | `"12345678"` | Mã nhận diện riêng cho bảng này, do nhà sản xuất đặt ra. Hãy sử dụng 8 ký tự cho mục này.                                                                                                            |
| **OEMRevision**        | `0x00000000` | Số hiệu phiên bản của bảng, do nhà sản xuất đặt ra. (định dạng 32-bit)                                                                                                                               |

<details>

<summary>32 bit number là gì?</summary>

Số nguyên `32-bit` là một số nguyên (`integer`) được biểu diễn bằng `32` chữ số nhị phân (`bits`). Mỗi bit có thể là `0` hoặc `1`, tạo ra tổng cộng `2^32 (4.294.967.296)` giá trị khác nhau.

Để dễ hiểu hơn, bạn có thể tưởng tượng như thế này:

* **Bit:** Giống như một bóng đèn, chỉ có thể bật `(1)` hoặc tắt `(0)`.
* **Số nguyên:** Là một con số "đếm" bình thường, ví dụ: 1, 2, 5, 10, -3,...
* **Số nguyên 32-bit:** Tương tự như việc bạn có `32` bóng đèn để biểu diễn một con số. Mỗi cách "bật/tắt" `32` bóng đèn này sẽ tượng trưng cho một giá trị số nguyên khác nhau.

**Ví dụ:**

* Số 10 trong hệ thập phân, khi được biểu diễn bằng số nguyên 32-bit sẽ là: `00000000 00000000 00000000 00001010`
* Số -5 trong hệ thập phân, khi được biểu diễn bằng số nguyên 32-bit (dạng bù 2) sẽ là: `11111111 11111111 11111111 11111011`

**Lợi ích:**

Sử dụng số nguyên `32-bit` giúp:

* **Biểu diễn được nhiều giá trị hơn:** So với việc chỉ dùng `8-bit` hay `16-bit`.
* **Thực hiện các phép toán nhanh hơn:** Vì máy tính xử lý dữ liệu theo đơn vị bit.

**Lưu ý:**

Ngày nay, với kiến trúc `64-bit` phổ biến, chúng ta có thể sử dụng số nguyên `64-bit` để biểu diễn phạm vi giá trị còn lớn hơn nữa.

</details>

{% hint style="success" %}
Dưới đây là một số ví dụ
{% endhint %}

```
DefinitionBlock ("", "SSDT", 2, "Hack", "CpuPlug", 0x00000000)
{

	// Viết code ở đây
	
} // Kết thúc code

// "" : AMLFileName
// "SSDT" : TableSignature
// 2: ComplianceRevision (Do hệ thống 64-bit)
// "hack":  OEM ID (tên tác giả)
// "CpuPlug": OEM Table ID Cách SSDT được nhận diện trong môi trường ACPI (Nó không phải tên tệp)
	// Thông thường được đặt theo Device hoặc Method mà SSDT đó xử lý và đối với ví dụ này nó là CpuPlug
 
```

{% hint style="danger" %}
Nếu bạn viết bảng thay thế (ví dụ: cho khai báo cổng USB), bạn cần sử dụng **cùng một** OEM Table ID với bảng bạn muốn thay thế.

> Nghe hơi khó hiểu nhưng cụ thể là nếu bạn muốn viết một SSDT thay thế cho một bảng ACPI nào khác ví dụ như một SSDT khác hay một DSDT khác thì phải cùng OEM Table ID
>
> > Hiểu chi tiết những gì mình nói xem [tại đây](https://advance.heavietnam.com/acpi-advance/map-usb-via-ssdt)
> > {% endhint %}

### Control Method

{% hint style="info" %}
Quy tắc đặc tên

Trong ASL, khi bạn đặt tên cho method và biến, hãy tránh dùng dấu gạch dưới `_` ở đầu.&#x20;

> Do chúng được "đánh dấu" là của hệ điều hành.&#x20;

Nếu sau khi dịch ngược bảng ASL mà bạn thấy cảnh báo \_T\_X

> Thì rất có thể bạn đã "đụng hàng" tên với hệ thống
> {% endhint %}

{% hint style="success" %}
Mối quan hệ của Method và Scope

Một `Method` phải luôn nằm bên trong một `Scope` của một `Device` nào đó.&#x20;

> Do đó, ví dụ dưới đây không hợp lệ vì `Method` được đặt ngay sau `DefinitionBlock`, trong khi `DefinitionBlock` không phải là một `Scope`
> {% endhint %}

{% hint style="success" %}
Mối quan hệ của `Method` và `Device`

* **`Method` không chứa `Device`**
  * `Method` không thể định nghĩa hoặc khai báo `Device` trực tiếp bên trong nó.&#x20;
  * `Device` phải được khai báo trong một `Scope`.
* **`Method` thường nằm trong `Scope` của `Device`**
  * Trong hầu hết trường hợp, `Method` được đặt bên trong `Scope` của một `Device` cụ thể.
  * Điều này cho phép `Method` truy cập và thao tác với các thuộc tính và chức năng của `Device` đó một cách dễ dàng.
* **`Method` thao tác với `Device` thế nào:**&#x20;
  * Bên trong `Method`, bạn có thể sử dụng tên của `Device`&#x20;
    * Nếu nó nằm trong cùng `Scope` hoặc `Scope` cha để:
      * **Đọc và ghi giá trị thuộc tính của `Device`.** Ví dụ, bạn có thể đọc trạng thái hiện tại của `Device` hoặc thay đổi cấu hình của nó.
      * **Gọi các `Method` khác được định nghĩa trong `Device`.** Điều này cho phép bạn thực hiện các tác vụ phức tạp hơn với `Device`.
        {% endhint %}

<pre><code><strong>// Một đoạn code sai cấu trúc 
</strong><strong>Method (xxxx, 0, NotSerialized)
</strong>	{
 	...
	}
DefinitionBlock ("xxxx", "DSDT", 0x02, "xxxx", "xxxx", xxxx)
{
		...
}
// Hoặc
DefinitionBlock ("", "SSDT", 2, "Example", "Example", 1)
{
    Method (_STA, 0, NotSerialized) // Sai rồi nhé!   Method phải nằm trong Scope cơ!
    {
        // ...
    }
}
</code></pre>

{% hint style="success" %}
Các Scope gốc

`\_GPE,\_PR,\_SB,\_SI,\_TZ` thuộc phạm vị root scope `/`&#x20;
{% endhint %}

<details>

<summary>Chi tiết chức năng </summary>

* **\\\_GPE**: **General Purpose Event** - Phạm vi dành cho các bộ xử lý sự kiện mục đích chung. Nơi định nghĩa các phương thức xử lý các sự kiện ngắt, thông báo từ phần cứng.
* **\\\_PR**: **Processor** - Phạm vi dành riêng cho bộ xử lý (CPU). Chứa thông tin về cấu hình, trạng thái, và các phương thức điều khiển CPU.
* **\\\_SB**: **System Bus** - Phạm vi cho các thiết bị và bus hệ thống. Hầu hết các thiết bị ngoại vi (ổ cứng, card mạng, USB...) sẽ được khai báo trong \\\_SB hoặc các Scope con của nó.
* **\\\_SI**: **System Indicator** - Phạm vi cho các chỉ báo hệ thống như đèn LED, màn hình LCD nhỏ... Cho phép điều khiển trạng thái hiển thị của các chỉ báo này.
* **\\\_TZ**: **Thermal Zone** - Phạm vi cho các vùng nhiệt. Chứa thông tin về cảm biến nhiệt độ, quạt tản nhiệt, và các phương thức điều khiển nhiệt độ hệ thống.

</details>

{% hint style="info" %}
Trong `ASL`, các thành phần với thuộc tính khác nhau được đặt bên trong các `Scope` tương ứng để tổ chức mã nguồn một cách logic và dễ quản lý.
{% endhint %}

{% hint style="success" %}
**Ví dụ:**

* Device (PCI0) (Thiết bị PCI0) sẽ được đặt bên trong Scope (\_SB) (Phạm vi System Bus) vì PCI là một loại bus hệ thống.
* Processor (CPU0, ...) (Bộ xử lý CPU0) sẽ được đặt bên trong Scope (\_PR) (Phạm vi Processor).
* Các phương thức xử lý ngắt từ bàn phím (Method (\_KBD, ...) ) sẽ được đặt bên trong Scope (\_GPE) (Phạm vi General Purpose Event).
  {% endhint %}

{% hint style="success" %}
**Lợi ích của việc tổ chức theo Scope:**

* **Dễ đọc, dễ hiểu:** Mã nguồn rõ ràng, dễ dàng tìm kiếm và theo dõi các thành phần liên quan.
* **Tránh xung đột tên:** Các thành phần có thể có cùng tên nhưng nằm trong Scope khác nhau, giúp tránh nhầm lẫn.
* **Quản lý quyền truy cập:** Scope kiểm soát phạm vi nhìn thấy của các tên, giúp bảo vệ dữ liệu và ngăn chặn lỗi sửa đổi ngoài ý muốn.
  {% endhint %}

```
// Device (PCI0) nằm bên trong Scope (\_SB)
Scope (\_SB)
{
	Device (PCI0)
	{
		...
	}
		...
}

// Thông tin liên quan đến CPU nằm bên trong Scope(_PR)
Scope (_PR)
{
    Processor (CPU0, 0x00, 0x00000410, 0x06)
    {
        ...
    }
    ...
}

// Scope (_GPE) nơi xử lý các sự kiện 
Scope (_GPE)
{
    Method (_L0D, 0, NotSerialized)
    {
        ...
    }
    ...
}
```

{% hint style="danger" %}
Nhắc lại một lần nữa

Các phương thức (Methods) và biến (variables) bắt đầu bằng dấu gạch dưới (\_) được dành riêng cho các hệ điều hành.
{% endhint %}

{% hint style="success" %}

#### Nhận diện Device

Device (xxxx) cũng có thể được nhận diện như một scope, nó chứa các mô tả về thiết bị, ví dụ: \_ADR, \_CID, \_UID, \_DSM, \_STA.

> **Giải thích**: Trong ASL, một thiết bị (Device) cũng có thể hoạt động như một phạm vi (scope). Bên trong phạm vi này, các mô tả chi tiết về thiết bị có thể được định nghĩa. Các mô tả này bao gồm:
>
> * **\_ADR**: Địa chỉ của thiết bị.
> * **\_CID**: Mã nhận diện tương thích của thiết bị.
> * **\_UID**: Mã nhận diện duy nhất của thiết bị.
> * **\_DSM**: Phương thức đặc biệt dành cho thiết bị.
> * **\_STA**: Trạng thái của thiết bị.
>   {% endhint %}

{% hint style="success" %}

#### Ký hiệu Scope

Ký hiệu \ trích dẫn root scope

Ký hiệu ^ trích dẫn superior scope.

Tương tự, ^^ đại diện cho phạm vi cao hơn một cấp nữa so với ^

> **Giải thích**: Trong ASL, ký hiệu \ được sử dụng để trích dẫn root scope
>
> > &#x20;Tức là phạm vi gốc của toàn bộ hệ thống.
>
> &#x20;Ký hiệu ^ được dùng để trích dẫn superior scope
>
> > Tức là phạm vi cấp trên so với phạm vi hiện tại.&#x20;
>
> Khi sử dụng nhiều dấu ^ liên tiếp, mỗi dấu đại diện cho một cấp độ phạm vi cao hơn.
> {% endhint %}

{% hint style="success" %}

#### Ký hiệu `_`

Ký hiệu `_` không có ý nghĩa, nó chỉ để hoàn thành đủ 4 ký tự, ví dụ: `_OSI.`

> **Giải thích**: Trong ASL, ký hiệu \_ không có ý nghĩa đặc biệt và thường được sử dụng chỉ để hoàn thành đủ 4 ký tự cho tên biến hoặc phương thức.&#x20;
>
> > Ví dụ: \_OSI là một tên phương thức hoàn chỉnh với 4 ký tự.
> > {% endhint %}

{% hint style="success" %}

#### ASL+ (ASL2.0)

Bảng ACPI 2.0, nó giới thiệu các toán tử của ngôn ngữ C như +-\*/=, <<, >> và các phép so sánh logic ==, !=, v.v.

> **Giải thích**: ASL+ (phiên bản 2.0 của ASL) mở rộng cú pháp của ASL, bao gồm việc giới thiệu các toán tử toán học và logic của ngôn ngữ lập trình C. Các toán tử này bao gồm:
>
> * **Toán tử số học**: +, -, \*, /, = (cộng, trừ, nhân, chia, gán)
> * **Toán tử dịch bit**: <<, >> (dịch trái, dịch phải)
> * **Toán tử logic**: ==, != (bằng, không bằng)
>   {% endhint %}

{% hint style="success" %}

#### Method trong ASL

Các phương thức (Methods) trong ASL có thể chấp nhận tối đa 7 tham số; chúng được biểu diễn bằng Arg0 đến Arg6 và không thể tùy chỉnh.

> **Giải thích**: Trong ASL, một phương thức (method) có thể nhận đến 7 tham số đầu vào. Các tham số này được đặt tên từ Arg0 đến Arg6 và không thể đổi tên hoặc tùy chỉnh. Điều này có nghĩa là khi viết phương thức, bạn chỉ có thể sử dụng các tham số đã được định sẵn này.
> {% endhint %}

{% hint style="success" %}

#### Local variables trong ASL

Các biến cục bộ (Local variables) trong ASL có thể chấp nhận tối đa 8 tham số, được biểu diễn bằng Local0 đến Local7. Các định nghĩa không cần thiết, nhưng phải được khởi tạo, nói cách khác, cần có sự gán giá trị.

> **Giải thích**: Trong ASL, các biến cục bộ có thể nhận đến 8 tham số, được đặt tên từ Local0 đến Local7. Không cần thiết phải định nghĩa các biến này trước khi sử dụng, nhưng chúng phải được khởi tạo bằng cách gán giá trị ban đầu. Điều này có nghĩa là bạn phải gán giá trị cho biến trước khi sử dụng nó trong phương thức.
> {% endhint %}

### Các khai báo trong `External`

{% hint style="info" %}
Các External references phải được sử dụng để truy cập các đối tượng trong DSDT, nơi chúng được định nghĩa. Nói cách khác: bạn cần thông báo cho SSDT vị trí của Device, Method hoặc bất kỳ đối tượng nào mà bạn muốn thay đổi trong cấu trúc của DSDT.
{% endhint %}

{% hint style="info" %}
Các card dưới đây sẽ tuân theo quy tắc sau:

* Dòng đầu Quote Types
* Dòng tiếp theo External Reference
* Dòng kế đó **Addressed Parameter**
* **Dòng cuối cùng là Giải thích**

**Ví dụ:**

<img src="/files/ofK7cNy2Y5xs1ZPUeP57" alt="" data-size="original">\
Do giới hạn phần mềm nên các bạn thông cảm cho mình phần này
{% endhint %}

<table data-card-size="large" data-view="cards" data-full-width="true"><thead><tr><th align="center">Quote Types</th><th>External Reference</th><th>Addressed paramter</th><th>Giải thích</th><th></th></tr></thead><tbody><tr><td align="center">UnknownObj</td><td><code>External (\_SB.EROR, UnknownObj</code></td><td>Tránh sử dụng</td><td>Đối tượng không xác định (UnknownObj) thường nên tránh sử dụng do không rõ ràng và có thể gây ra lỗi hoặc khó khăn trong việc xử lý.</td><td></td></tr><tr><td align="center">IntObj</td><td><code>External (TEST, IntObj</code></td><td><code>Name (TEST, 0)</code></td><td>Đối tượng số nguyên (IntObj) có thể được tham chiếu bằng cách sử dụng tên và giá trị của nó.</td><td></td></tr><tr><td align="center">StrObj</td><td><code>External (\_PR.MSTR, StrObj</code></td><td><code>Name (MSTR,"ASL")</code></td><td>Đối tượng chuỗi (StrObj) có thể được tham chiếu bằng tên và giá trị chuỗi của nó</td><td></td></tr><tr><td align="center">BuffObj</td><td><code>External (\_SB.PCI0.I2C0.TPD0.SBFB, BuffObj</code></td><td><code>Name (SBFB, ResourceTemplate ()</code><br><code>Name (BUF0, Buffer() {"abcde"})</code></td><td>Đối tượng bộ đệm (BuffObj) có thể được tham chiếu bằng tên và nội dung của nó</td><td></td></tr><tr><td align="center">PkgObj</td><td><code>External (_SB.PCI0.RP01._PRW, PkgObj</code></td><td><code>Name (_PRW, Package (0x02) { 0x0D, 0x03 })</code></td><td>Đối tượng gói (PkgObj) có thể được tham chiếu bằng tên và nội dung của gói</td><td></td></tr><tr><td align="center">FieldUnitObj</td><td><code>External (OSYS, FieldUnitObj</code></td><td><code>OSYS, 16,</code></td><td>Đối tượng đơn vị trường (FieldUnitObj) có thể được tham chiếu bằng tên và kích thước của nó.</td><td></td></tr><tr><td align="center">DeviceObj</td><td><code>External (\_SB.PCI0.I2C1.ETPD, DeviceObj</code></td><td><code>Device (ETPD)</code></td><td>Đối tượng thiết bị (DeviceObj) có thể được tham chiếu bằng tên của thiết bị</td><td></td></tr><tr><td align="center">EventObj</td><td><code>External (XXXX, EventObj</code></td><td><code>Event (XXXX)</code></td><td>Đối tượng sự kiện (EventObj) có thể được tham chiếu bằng tên sự kiện.</td><td></td></tr><tr><td align="center">MethodObj</td><td><code>External (\_SB.PCI0.GPI0._STA, MethodObj</code></td><td><code>Method (_STA, 0, NotSerialized)</code></td><td>Đối tượng phương thức (MethodObj) có thể được tham chiếu bằng tên và các thuộc tính của phương thức.</td><td></td></tr><tr><td align="center">MutexObj</td><td><code>External (_SB.PCI0.LPCB.EC0.BATM, MutexObj</code></td><td><code>Mutex (BATM, 0x07)</code></td><td>Đối tượng mutex (MutexObj) có thể được tham chiếu bằng tên và giá trị của mutex.</td><td></td></tr><tr><td align="center">OpRegionObj</td><td><code>External (GNVS, OpRegionObj</code></td><td><code>OperationRegion (GNVS, SystemMemory, 0x7A4E7000, 0x0866)</code></td><td>Đối tượng vùng hoạt động (OpRegionObj) có thể được tham chiếu bằng tên, loại bộ nhớ và địa chỉ của nó.</td><td></td></tr><tr><td align="center">PowerResObj</td><td><code>External (\_SB.PCI0.XDCI, PowerResObj</code></td><td><code>PowerResource (USBC, 0, 0)</code></td><td>Đối tượng nguồn điện (PowerResObj) có thể được tham chiếu bằng tên và các thuộc tính của nguồn điện.</td><td></td></tr><tr><td align="center">ProcessorObj</td><td><code>External (\_SB.PR00, ProcessorObj</code></td><td><code>Processor (PR00, 0x01, 0x00001810, 0x06)</code></td><td>Đối tượng bộ xử lý (ProcessorObj) có thể được tham chiếu bằng tên và các thuộc tính của bộ xử lý.</td><td></td></tr><tr><td align="center">ThermalZoneObj</td><td><code>External (\_TZ.THRM, ThermalZoneObj</code></td><td><code>ThermalZone (THRM)</code></td><td>Đối tượng vùng nhiệt (ThermalZoneObj) có thể được tham chiếu bằng tên của vùng nhiệt.</td><td></td></tr><tr><td align="center">BuffFieldObj</td><td><code>External (\_SB.PCI0._CRS.BBBB, BuffFieldObj</code></td><td><code>CreateField (AAAA, Zero, BBBB)</code></td><td>Đối tượng trường bộ đệm (BuffFieldObj) có thể được tham chiếu bằng tên và các thuộc tính của trường bộ đệm.</td><td></td></tr></tbody></table>

<details>

<summary>Giải thích thuật ngữ tham chiếu</summary>

Trong ngữ cảnh của ACPI (Advanced Configuration and Power Interface) và ASL (ACPI Source Language), "tham chiếu" đề cập đến việc chỉ ra hoặc truy cập một đối tượng cụ thể được định nghĩa ở một nơi khác trong cấu trúc DSDT (Differentiated System Description Table) hoặc SSDT (Secondary System Description Table).

#### Giải thích chi tiết về tham chiếu:

1. **Tham chiếu ngoài (External Reference)**:
   * **Định nghĩa**: Tham chiếu ngoài là cách bạn nói với một bảng SSDT vị trí của một đối tượng (ví dụ: Device, Method, Field, v.v.) đã được định nghĩa trong bảng DSDT hoặc một bảng SSDT khác. Điều này giúp bạn truy cập và thao tác các đối tượng đó mà không cần phải định nghĩa lại chúng.
   * **Ví dụ**: External (\_SB.PCI0.\_CRS.BBBB, BuffFieldObj)
2. **Ví dụ chi tiết về tham chiếu ngoài**:
   * **External (\_SB.PCI0.\_CRS.BBBB, BuffFieldObj)**:
     * **External**: Từ khóa này chỉ ra rằng bạn đang tạo một tham chiếu ngoài.
     * **\_SB.PCI0.\_CRS.BBBB**: Đây là đường dẫn đầy đủ đến đối tượng trường bộ đệm (BuffFieldObj) trong DSDT. Nó chỉ ra rằng đối tượng BBBB nằm trong vùng \_CRS của thiết bị PCI0, thuộc hệ thống bus \_SB.
     * **BuffFieldObj**: Đây là loại của đối tượng bạn đang tham chiếu, trong trường hợp này là một đối tượng trường bộ đệm.

#### Tóm lại:

Tham chiếu (reference) trong ASL là một cách để chỉ ra hoặc truy cập một đối tượng cụ thể đã được định nghĩa ở nơi khác. Điều này giúp bạn quản lý và sử dụng các đối tượng trong cấu trúc DSDT và SSDT mà không cần phải định nghĩa lại chúng, đảm bảo tính nhất quán và tái sử dụng các định nghĩa đã có.

</details>

### ACPI Preset Functions

{% hint style="info" %}
ACPI Preset Functions là các phương thức và giao diện được xác định sẵn trong ACPI&#x20;

> Để hỗ trợ cấu hình và quản lý tài nguyên phần cứng của hệ thống.&#x20;
>
> > Các chức năng này cho phép hệ điều hành và firmware giao tiếp và kiểm soát các thiết bị phần cứng một cách linh hoạt và hiệu quả.
> > {% endhint %}

#### **`_OSI` (Operating System Interfaces)**

{% hint style="info" %}
Method\_OSI (Giao diện Hệ điều hành) dễ dàng lấy được tên và phiên bản của hệ điều hành hiện tại.&#x20;

> Ví dụ, chúng ta có thể áp dụng một bản vá cụ thể cho Windows hoặc macOS.
> {% endhint %}

{% hint style="warning" %}
Giá trị cho các Method \_OSI phải được chọn từ bảng dưới đây
{% endhint %}

| Hệ điều hành                               | Chuỗi          |
| ------------------------------------------ | -------------- |
| macOS                                      | "Darwin"       |
| Linux (và các hệ điều hành dựa trên Linux) | "Linux"        |
| FreeBSD                                    | "FreeBSD"      |
| Windows                                    | "Windows 20XX" |

{% hint style="danger" %}
Lưu ý

Các phiên bản Windows khác nhau yêu cầu một chuỗi riêng biệt

> Đọc thêm tại: [WinACPI-OSI Documentation](https://docs.microsoft.com/en-us/windows-hardware/drivers/acpi/winacpi-osi).
> {% endhint %}

```
// Khi chuỗi của _OSI khớp với hệ thống hiện tại, nó sẽ trả về 1 vì điều kiện If là đúng.

If (_OSI ("Darwin")) /* Kiểm tra nếu hệ thống hiện tại là macOS */
```

#### **`_STA` (Status)**

{% hint style="warning" %}
Có hai loại `_STA`&#x20;

Đừng nhầm lẫn với \_STA từ PowerResource!
{% endhint %}

{% hint style="info" %}
Phương thức \_STA có thể trả về 5 loại bit, giải thích như sau
{% endhint %}

| Bit      | Giải thích                                                                        |
| -------- | --------------------------------------------------------------------------------- |
| Bit \[0] | Được đặt nếu thiết bị hiện diện.                                                  |
| Bit \[1] | Được đặt nếu thiết bị được kích hoạt và giải mã tài nguyên của nó.                |
| Bit \[2] | Được đặt nếu thiết bị nên được hiển thị trong giao diện người dùng.               |
| Bit \[3] | Được đặt nếu thiết bị hoạt động đúng (xóa nếu thiết bị không vượt qua chẩn đoán). |
| Bit \[4] | Được đặt nếu pin hiện diện.                                                       |

{% hint style="info" %}
Chúng ta cần chuyển đổi các bit này từ hệ thập lục phân sang hệ nhị phân.

> Ví dụ, 0x0F chuyển thành 1111, có nghĩa là kích hoạt nó (bốn bit đầu tiên)
>
> Trong khi Zero có nghĩa là vô hiệu hóa.
> {% endhint %}

{% hint style="info" %}
Chúng ta cũng gặp 0x0B và 0x1F.&#x20;

> Dạng nhị phân của 0x0B là 1011
>
> > Có nghĩa là thiết bị được kích hoạt nhưng không được phép giải mã tài nguyên của nó.
> >
> > > 0x0B thường được sử dụng trong SSDT-PNLF.&#x20;

0x1F (11111) chỉ xuất hiện để mô tả các thiết bị pin trên máy tính xách tay

> Bit cuối cùng được sử dụng để thông báo cho thiết bị Pin Điều khiển Phương thức PNP0C0A rằng pin hiện diện.

Có lẽ vẫn hơi mơ hồ hãy đọc phần giiar thích chi tiết sau nhé
{% endhint %}

#### Giải thích cho Method `_STA`

{% hint style="info" %}
Trong biểu diễn nhị phân, các bit được đánh số từ phải sang trái, bắt đầu từ 0

> Dưới đây là cách phân tích chi tiết giá trị nhị phân `00001111` để xác định từng bit và ý nghĩa của chúng:
> {% endhint %}

{% hint style="info" %}

#### Ở ví dụ này chúng ta sẽ lấy giá trị nhị phân `00001111`

{% endhint %}

**Vị trí các bit:**

```bash
Bit vị trí: 7  6  5  4  3  2  1  0
Giá trị:    0  0  0  0  1  1  1  1
```

#### Ý nghĩa từng bit

<table><thead><tr><th width="102">Bit</th><th width="94">Vị trí</th><th width="82">Giá trị</th><th>Ý nghĩa</th></tr></thead><tbody><tr><td>Bit [0]</td><td>0</td><td>1</td><td>Được đặt (1) nếu thiết bị hiện diện.</td></tr><tr><td>Bit [1]</td><td>1</td><td>1</td><td>Được đặt (1) nếu thiết bị được kích hoạt và giải mã tài nguyên của nó.</td></tr><tr><td>Bit [2]</td><td>2</td><td>1</td><td>Được đặt (1) nếu thiết bị nên được hiển thị trong giao diện người dùng.</td></tr><tr><td>Bit [3]</td><td>3</td><td>1</td><td>Được đặt (1) nếu thiết bị hoạt động đúng (xóa nếu thiết bị không vượt qua chẩn đoán).</td></tr><tr><td>Bit [4]</td><td>4</td><td>0</td><td>Được đặt (1) nếu pin hiện diện.</td></tr><tr><td>Bit [5]</td><td>5</td><td>0</td><td>Không sử dụng trong ví dụ này.</td></tr><tr><td>Bit [6]</td><td>6</td><td>0</td><td>Không sử dụng trong ví dụ này.</td></tr><tr><td>Bit [7]</td><td>7</td><td>0</td><td>Không sử dụng trong ví dụ này.</td></tr></tbody></table>

<details>

<summary>Diễn giải chi tiết của 00001111 (0x0F)</summary>

* **Bit \[0]** = 1: Thiết bị hiện diện
* **Bit \[1]** = 1: Thiết bị được kích hoạt và giải mã tài nguyên của nó
* **Bit \[2]** = 1: Thiết bị nên được hiển thị trong giao diện người dùng
* **Bit \[3]** = 1: Thiết bị hoạt động đúng (không có lỗi chẩn đoán)
* **Bit \[4]** = 0: Pin không hiện diện
* **Bit \[5]** = 0: Không sử dụng trong ví dụ này
* **Bit \[6]** = 0: Không sử dụng trong ví dụ này
* **Bit \[7]** = 0: Không sử dụng trong ví dụ này

</details>

{% hint style="info" %}
Tổng kết

* **Bit \[0]** là bit ở vị trí ngoài cùng bên phải.
* **Bit \[1]** là bit ngay cạnh bên trái của bit \[0].
  {% endhint %}

<details>

<summary>Giới thiệu qua về _STA trong PowerResource</summary>

\_STA từ PowerResource chỉ trả về Một hoặc Zero

</details>

#### **`_CRS` (Current Resource Settings)**

{% hint style="info" %}
`_CRS` trả về một Bộ đệm (`Buffer`)

> Thường được sử dụng để lấy các thiết bị cảm ứng như GPIO Pin, APIC Pin để điều khiển chế độ ngắt.
> {% endhint %}

### Các kiểu dữ liệu cơ bản trong ASL

<table data-full-width="false"><thead><tr><th align="center">ASL</th></tr></thead><tbody><tr><td align="center"><code>Integer</code></td></tr><tr><td align="center"><code>String</code></td></tr><tr><td align="center"><code>Event</code></td></tr><tr><td align="center"><code>Buffer</code></td></tr><tr><td align="center"><code>Package</code></td></tr></tbody></table>

### Định nghĩa Biến trong ASL

#### **Định nghĩa Integer (Số nguyên)**

```asl
Name (TEST, 0)
```

* **Name (TEST, 0)**: Định nghĩa một biến số nguyên có tên là `TEST` với giá trị ban đầu là `0`.

#### **Định nghĩa String (Chuỗi)**

```asl
Name (MSTR, "ASL")
```

* **Name (MSTR, "ASL")**: Định nghĩa một biến chuỗi có tên là `MSTR` với giá trị ban đầu là `"ASL"`.

#### **Định nghĩa Package (Gói)**

```asl
Name (_PRW, Package (0x02)
{
    0x0D,
    0x03
})
```

* **Name (\_PRW, Package (0x02) { ... })**: Định nghĩa một biến gói (package) có tên là `_PRW` với hai phần tử bên trong là `0x0D` và `0x03`.

#### Định nghĩa Buffer Field (Trường bộ đệm)

{% hint style="info" %}
Có 6 loại trường bộ đệm có sẵn trong ASL, mỗi loại có kích thước khác nhau và cú pháp riêng biệt.
{% endhint %}

{% hint style="warning" %}
Các số trong cập ( ) mình dùng ở đây là size của field
{% endhint %}

**CreateBitField (1-Bit)**

```asl
CreateBitField (AAAA, Zero, CCCC)
```

* **CreateBitField (AAAA, Zero, CCCC)**: Tạo một trường bộ đệm 1-bit từ bộ đệm `AAAA` bắt đầu từ vị trí `Zero` và đặt tên cho trường là `CCCC`.

**CreateByteField (8-Bit)**

```asl
CreateByteField (DDDD, 0x01, EEEE)
```

* **CreateByteField (DDDD, 0x01, EEEE)**: Tạo một trường bộ đệm 8-bit từ bộ đệm `DDDD` bắt đầu từ vị trí `0x01` và đặt tên cho trường là `EEEE`.

**CreateWordField (16-Bit)**

```asl
CreateWordField (FFFF, 0x05, GGGG)
```

* **CreateWordField (FFFF, 0x05, GGGG)**: Tạo một trường bộ đệm 16-bit từ bộ đệm `FFFF` bắt đầu từ vị trí `0x05` và đặt tên cho trường là `GGGG`.

**CreateDWordField (32-Bit)**

```asl
CreateDWordField (HHHH, 0x06, IIII)
```

* **CreateDWordField (HHHH, 0x06, IIII)**: Tạo một trường bộ đệm 32-bit từ bộ đệm `HHHH` bắt đầu từ vị trí `0x06` và đặt tên cho trường là `IIII`.

**CreateQWordField (64-Bit)**

```asl
CreateQWordField (JJJJ, 0x14, KKKK)
```

* **CreateQWordField (JJJJ, 0x14, KKKK)**: Tạo một trường bộ đệm 64-bit từ bộ đệm `JJJJ` bắt đầu từ vị trí `0x14` và đặt tên cho trường là `KKKK`.

**CreateField (Kích thước bất kỳ)**

```asl
CreateField (LLLL, Local0, 0x38, MMMM)
```

* **CreateField (LLLL, Local0, 0x38, MMMM)**: Tạo một trường bộ đệm với kích thước bất kỳ từ bộ đệm `LLLL`, bắt đầu từ vị trí `Local0`, có độ dài `0x38` và đặt tên cho trường là `MMMM`.

{% hint style="info" %}
Trong ASL  việc định nghĩa một biến không cần phải thông báo rõ ràng loại của biến.&#x20;

> Điều này có nghĩa là bạn có thể định nghĩa các biến mà không cần chỉ rõ chúng là số nguyên, chuỗi, gói, hay bộ đệm.&#x20;
>
> ASL sẽ tự động xử lý và xác định loại biến dựa trên cách biến đó được sử dụng và giá trị được gán cho nó.
>
> > Ví dụ: `Name (TEST, 0)`
> >
> > Các bạn có thể thấy biến `Test` được khai báo nhưng không chỉ rõ loại biến. Tuy vậy do giá trị ban đầu là `0` nên `ASL` sẽ hiểu đây là biến có kiểu dữ liệu nguyên (`integer)`
> > {% endhint %}

### Gán giá trị trong ASL

{% hint style="info" %}
Trong ASL, việc gán giá trị cho các biến và đối tượng rất quan trọng để thiết lập và thay đổi trạng thái của hệ thống phần cứng.&#x20;

> Dưới đây là cách sử dụng các lệnh gán giá trị trong ASL
> {% endhint %}

```
// Cú pháp
Store (a,b) /* legacy ASL */
b = a      /*   ASL+  */

// Ví dụ

Store (0, Local0)
Local0 = 0

Store (Local0, Local1)
Local1 = Local0
```

### Các phép tính trong ASL

#### Bảng Toán Tử trong ASL và Legacy ASL

| ASL+ | Legacy ASL | Examples                                                                                              | Giải thích                                                                                                             |                                                                                                       |
| ---- | ---------- | ----------------------------------------------------------------------------------------------------- | ---------------------------------------------------------------------------------------------------------------------- | ----------------------------------------------------------------------------------------------------- |
| +    | Add        | <p><code>Local0 = 1 + 2</code><br><code>Add (1, 2, Local0)</code></p>                                 | `Add (1, 2, Local0)` - Cộng hai số và gán kết quả vào `Local0`.                                                        |                                                                                                       |
| \*   | Multiply   | <p><code>Local0 = 1 \* 2</code><br><code>Multiply (1, 2, Local0)</code></p>                           | `Multiply (1, 2, Local0)` - Nhân hai số và gán kết quả vào `Local0`.                                                   |                                                                                                       |
| /    | Divide     | <p><code>Local0 = 10 / 9</code><br><code>Divide (10, 9, Local1(remainder), Local0(result))</code></p> | `Divide (10, 9, Local1(remainder), Local0(result))` - Chia 10 cho 9, gán kết quả vào `Local0` và phần dư vào `Local1`. |                                                                                                       |
| %    | Mod        | <p><code>Local0 = 10 % 9</code><br><code>Mod (10, 9, Local0)</code></p>                               | `Mod (10, 9, Local0)` - Lấy phần dư của phép chia 10 cho 9 và gán vào `Local0`.                                        |                                                                                                       |
| <<   | ShiftLeft  | <p><code>Local0 = 1 << 20</code><br><code>ShiftLeft (1, 20, Local0)</code></p>                        | `ShiftLeft (1, 20, Local0)` - Dịch chuyển bit của số 1 sang trái 20 vị trí và gán kết quả vào `Local0`.                |                                                                                                       |
| >>   | ShiftRight | <p><code>Local0 = 0x10000 >> 4</code><br><code>ShiftRight (0x10000, 4, Local0)</code></p>             | `ShiftRight (0x10000, 4, Local0)` - Dịch chuyển bit của số `0x10000` sang phải 4 vị trí và gán kết quả vào `Local0`.   |                                                                                                       |
| --   | Decrement  | <p><code>Local0--</code><br><code>Decrement (Local0)</code></p>                                       | `Decrement (Local0)` - Giảm giá trị của `Local0` đi 1.                                                                 |                                                                                                       |
| ++   | Increment  | <p><code>Local0++</code><br><code>Increment (Local0)</code></p>                                       | `Increment (Local0)` - Tăng giá trị của `Local0` lên 1.                                                                |                                                                                                       |
| &    | And        | <p><code>Local0 = 0x11 & 0x22</code><br><code>And (0x11, 0x22, Local0)</code></p>                     | `And (0x11, 0x22, Local0)` - Thực hiện phép toán AND giữa `0x11` và `0x22` và gán kết quả vào `Local0`.                |                                                                                                       |
| \|   | Or         | <p><code>Local0 = 0x01                                                                                | 0x02</code><br><code>Or (0x01, 0x02, Local0)</code></p>                                                                | `Or (0x01, 0x02, Local0)` - Thực hiện phép toán OR giữa `0x01` và `0x02` và gán kết quả vào `Local0`. |
| \~   | Not        | <p><code>Local0 = \~(0x00)</code><br><code>Not (0x00,Local0)</code></p>                               | `Not (0x00, Local0)` - Thực hiện phép toán NOT (đảo bit) trên `0x00` và gán kết quả vào `Local0`.                      |                                                                                                       |
|      | Nor        | `Nor (0x11, 0x22, Local0)`                                                                            | `Nor (0x11, 0x22, Local0)` - Thực hiện phép toán NOR giữa `0x11` và `0x22` và gán kết quả vào `Local0`.                |                                                                                                       |

<details>

<summary>Giải thích thêm về các toán tử</summary>

* **Add (Cộng)**:
  * **Cú pháp ASL+**: Local0 = 1 + 2
  * **Legacy ASL**: Add (1, 2, Local0)
  * **Giải thích**: Thực hiện phép cộng giữa hai số và gán kết quả vào `Local0`.
* **Multiply (Nhân)**:
  * **Cú pháp ASL+**: Local0 = 1 \* 2
  * **Legacy ASL**: Multiply (1, 2, Local0)
  * **Giải thích**: Thực hiện phép nhân giữa hai số và gán kết quả vào `Local0`.
* **Divide (Chia)**:
  * **Cú pháp ASL+**: Local0 = 10 / 9
  * **Legacy ASL**: Divide (10, 9, Local1(remainder), Local0(result))
  * **Giải thích**: Chia 10 cho 9, gán kết quả vào `Local0` và phần dư vào `Local1`.
* **Mod (Lấy phần dư)**:
  * **Cú pháp ASL+**: Local0 = 10 % 9
  * **Legacy ASL**: Mod (10, 9, Local0)
  * **Giải thích**: Lấy phần dư của phép chia 10 cho 9 và gán vào `Local0`.
* **ShiftLeft (Dịch trái)**:
  * **Cú pháp ASL+**: Local0 = 1 << 20
  * **Legacy ASL**: ShiftLeft (1, 20, Local0)
  * **Giải thích**: Dịch chuyển bit của số 1 sang trái 20 vị trí và gán kết quả vào `Local0`.
* **ShiftRight (Dịch phải)**:
  * **Cú pháp ASL+**: Local0 = 0x10000 >> 4
  * **Legacy ASL**: ShiftRight (0x10000, 4, Local0)
  * **Giải thích**: Dịch chuyển bit của số `0x10000` sang phải 4 vị trí và gán kết quả vào `Local0`.
* **Decrement (Giảm)**:
  * **Cú pháp ASL+**: Local0--
  * **Legacy ASL**: Decrement (Local0)
  * **Giải thích**: Giảm giá trị của `Local0` đi 1.
* **Increment (Tăng)**:
  * **Cú pháp ASL+**: Local0++
  * **Legacy ASL**: Increment (Local0)
  * **Giải thích**: Tăng giá trị của `Local0` lên 1.
* **And (AND bitwise)**:
  * **Cú pháp ASL+**: Local0 = 0x11 & 0x22
  * **Legacy ASL**: And (0x11, 0x22, Local0)
  * **Giải thích**: Thực hiện phép toán AND giữa `0x11` và `0x22` và gán kết quả vào `Local0`.
* **Or (OR bitwise)**:
  * **Cú pháp ASL+**: Local0 = 0x01 | 0x02
  * **Legacy ASL**: Or (0x01, 0x02, Local0)
  * **Giải thích**: Thực hiện phép toán OR giữa `0x01` và `0x02` và gán kết quả vào `Local0`.
* **Not (NOT bitwise)**:
  * **Cú pháp ASL+**: Local0 = \~(0x00)
  * **Legacy ASL**: Not (0x00, Local0)
  * **Giải thích**: Thực hiện phép toán NOT (đảo bit) trên `0x00` và gán kết quả vào `Local0`.
* **Nor (NOR bitwise)**:
  * **Legacy ASL**: Nor (0x11, 0x22, Local0)
  * **Giải thích**: Thực hiện phép toán NOR giữa `0x11` và `0x22` và gán kết quả vào `Local0`.

</details>

### ASL Logic

| ASL+ | Legacy ASL    | Examble                                                                                         | Giải thích                                                            |                                                                     |
| ---- | ------------- | ----------------------------------------------------------------------------------------------- | --------------------------------------------------------------------- | ------------------------------------------------------------------- |
| &&   | LAnd          | <p><code>If (BOL1 && BOL2)</code> </p><p><code>If (LAnd(BOL1, BOL2))</code></p>                 | Kiểm tra nếu cả `BOL1` và `BOL2` đều đúng (AND logic).                |                                                                     |
| !    | LNot          | <p><code>Local0 = !0</code></p><p><code>Store (LNot(0), Local0)</code></p>                      | Thực hiện phép toán NOT trên giá trị `0` và gán kết quả vào `Local0`. |                                                                     |
| \|   | LOr           | <p><code>Local0 = (0                                                                            | 1)</code> </p><p><code>Store (LOr(0, 1), Local0)</code></p>           | Thực hiện phép toán OR giữa `0` và `1` và gán kết quả vào `Local0`. |
| <    | LLess         | <p><code>Local0 = (1 < 2)</code></p><p><code>Store (LLess(1, 2), Local0)</code></p>             | Kiểm tra nếu `1` nhỏ hơn `2` và gán kết quả vào `Local0`.             |                                                                     |
| <=   | LLessEqual    | <p><code>Local0 = (1 <= 2)</code> </p><p><code>Store (LLessEqual(1, 2), Local0)</code></p>      | Kiểm tra nếu `1` nhỏ hơn hoặc bằng `2` và gán kết quả vào `Local0`.   |                                                                     |
| >    | LGreater      | <p><code>Local0 = (1 > 2)</code></p><p><code>Store (LGreater(1, 2), Local0)</code></p>          | Kiểm tra nếu `1` lớn hơn `2` và gán kết quả vào `Local0`.             |                                                                     |
| >=   | LGreaterEqual | <p><code>Local0 = (1 >= 2)</code> </p><p><code>Store (LGreaterEqual(1, 2), Local0)</code></p>   | Kiểm tra nếu `1` lớn hơn hoặc bằng `2` và gán kết quả vào `Local0`.   |                                                                     |
| ==   | LEqual        | <p><code>Local0 = (Local0 == Local1)</code> </p><p><code>If (LEqual(Local0, Local1))</code></p> | Kiểm tra nếu `Local0` bằng `Local1`.                                  |                                                                     |
| !=   | LNotEqual     | `Local0 = (0 != 1) Store (LNotEqual(0, 1), Local0)`                                             | Kiểm tra nếu `0` không bằng `1` và gán kết quả vào `Local0`.          |                                                                     |

<details>

<summary>Giải thích thêm về các toán tử logic</summary>

* **LAnd (AND logic)**:
  * **Cú pháp ASL+**: If (BOL1 && BOL2)
  * **Legacy ASL**: If (LAnd(BOL1, BOL2))
  * **Giải thích**: Kiểm tra nếu cả `BOL1` và `BOL2` đều đúng (AND logic).
* **LNot (NOT logic)**:
  * **Cú pháp ASL+**: Local0 = !0
  * **Legacy ASL**: Store (LNot(0), Local0)
  * **Giải thích**: Thực hiện phép toán NOT trên giá trị `0` và gán kết quả vào `Local0`.
* **LOr (OR logic)**:
  * **Cú pháp ASL+**: Local0 = (0|1)
  * **Legacy ASL**: Store (LOr(0, 1), Local0)
  * **Giải thích**: Thực hiện phép toán OR giữa `0` và `1` và gán kết quả vào `Local0`.
* **LLess (Nhỏ hơn)**:
  * **Cú pháp ASL+**: Local0 = (1 < 2)
  * **Legacy ASL**: Store (LLess(1, 2), Local0)
  * **Giải thích**: Kiểm tra nếu `1` nhỏ hơn `2` và gán kết quả vào `Local0`.
* **LLessEqual (Nhỏ hơn hoặc bằng)**:
  * **Cú pháp ASL+**: Local0 = (1 <= 2)
  * **Legacy ASL**: Store (LLessEqual(1, 2), Local0)
  * **Giải thích**: Kiểm tra nếu `1` nhỏ hơn hoặc bằng `2` và gán kết quả vào `Local0`.
* **LGreater (Lớn hơn)**:
  * **Cú pháp ASL+**: Local0 = (1 > 2)
  * **Legacy ASL**: Store (LGreater(1, 2), Local0)
  * **Giải thích**: Kiểm tra nếu `1` lớn hơn `2` và gán kết quả vào `Local0`.
* **LGreaterEqual (Lớn hơn hoặc bằng)**:
  * **Cú pháp ASL+**: Local0 = (1 >= 2)
  * **Legacy ASL**: Store (LGreaterEqual(1, 2), Local0)
  * **Giải thích**: Kiểm tra nếu `1` lớn hơn hoặc bằng `2` và gán kết quả vào `Local0`.
* **LEqual (Bằng nhau)**:
  * **Cú pháp ASL+**: Local0 = (Local0 == Local1)
  * **Legacy ASL**: If (LEqual(Local0, Local1))
  * **Giải thích**: Kiểm tra nếu `Local0` bằng `Local1`.
* **LNotEqual (Không bằng nhau)**:
  * **Cú pháp ASL+**: Local0 = (0 != 1)
  * **Legacy ASL**: Store (LNotEqual(0, 1), Local0)
  * **Giải thích**: Kiểm tra nếu `0` không bằng `1` và gán kết quả vào `Local0`.

</details>

{% hint style="info" %}
Các phép tính logic chỉ có hai kết quả là `0` hoặc `1`.
{% endhint %}

### Định nghĩa Method trong ASL

#### **Định nghĩa một Method**

```asl
Method (TEST)
{
    ...
}
```

* **Method (TEST)**: Định nghĩa một Method với tên `TEST`.

#### **Định nghĩa Method với 2 tham số và sử dụng Biến Cục bộ**

Số lượng tham số mặc định là 0. Bạn có thể định nghĩa Method với các tham số và sử dụng các biến cục bộ từ Local0 đến Local7.

```asl
Method (MADD, 2)
{
    Local0 = Arg0
    Local1 = Arg1
    Local0 += Local7
}
```

* **Method (MADD, 2)**: Định nghĩa một Method với tên `MADD` và 2 tham số.
* **Local0, Local1**: Sử dụng các biến cục bộ.
* **Arg0, Arg1**: Tham số đầu vào của Method.

#### **Định nghĩa Method có Giá trị Trả về**

```asl
Method (MADD, 2)
{
    Local0 = Arg0
    Local1 = Arg1
    Local0 += Local1

    Return (Local0) /* Trả về giá trị tại đây */
}
```

* **Return (Local0)**: Trả về giá trị của Local0.

```asl
// Ví dụ Sử dụng Method trong ASL+ và Legacy ASL
Local0 = 1 + 2            /* ASL+ */
Store (MADD (1, 2), Local0)  /* Legacy ASL */
```

* **Local0 = 1 + 2**: Ví dụ trong ASL+.
* **Store (MADD (1, 2), Local0)**: Ví dụ trong Legacy ASL
  * Gọi Method `MADD` với tham số 1 và 2,
  * Sau đó gán kết quả cho Local0.

#### **Định nghĩa Method được Tuần tự hóa**

{% hint style="info" %}
Nếu không định nghĩa `Serialized` hoặc `NotSerialized`, mặc định sẽ là `NotSerialized`.
{% endhint %}

```asl
Method (MADD, 2, Serialized)
{
    Local0 = Arg0
    Local1 = Arg1
    Local0 += Local1
    Return (Local0)
}
```

* **Serialized**: Chỉ định rằng Method được tuần tự hóa.&#x20;
  * Điều này có nghĩa là chỉ một thể hiện (instance) của Method đó có thể tồn tại và thực thi trong bộ nhớ tại một thời điểm.&#x20;
  * Nói cách khác, các lần gọi Method đó sẽ được thực hiện tuần tự, không đồng thời.
* Nếu Method không được chỉ định là `Serialized`
  * Mặc định là `NotSerialized`
    * Các lần gọi Method này có thể thực thi đồng thời
    * &#x20;Điều này có thể dẫn đến xung đột nếu các lần gọi này cùng tạo hoặc thay đổi cùng một tài nguyên.

{% hint style="info" %}
Ví dụ về Method Serialized
{% endhint %}

```asl
Method (TEST, Serialized)
{
    Name (MSTR, "I will succeed")
}
```

* **Method TEST**: Nếu gọi từ hai Method khác nhau:

```asl
Device (Dev1)
{
    TEST ()
}
Device (Dev2)
{
    TEST ()
}
```

* Khi thực thi `TEST` trong `Dev1`
  * &#x20;`TEST` trong `Dev2` sẽ phải chờ cho đến khi `TEST` trong `Dev1` hoàn tất.

{% hint style="info" %}
Ví dụ về Method NotSerialized
{% endhint %}

```asl
Method (TEST, NotSerialized)
{
    Name (MSTR, "I will succeed")
}
```

* **Method TEST**: Nếu gọi từ hai Method khác nhau:

```
Device (Dev1)
{
    Method (CALL_TEST1)
    {
        TEST()  // Có thể thất bại
    }
}

Device (Dev2)
{
    Method (CALL_TEST2)
    {
        TEST()  // Có thể thất bại
    }
}
```

* Nếu một trong các `TEST` được gọi từ `Devx`, một `TEST` khác sẽ không thể tạo `MSTR`, dẫn đến thất bại.
* Cụ thể :&#x20;
  * Khi cả hai phương thức `TEST()` đang chạy đồng thời, cả hai đều cố gắng tạo biến `MSTR` cùng một lúc.
  * Vì `MSTR` không thể được tạo đồng thời trong hai Method không tuần tự hóa, dẫn đến xung đột.

### Kiểm Soát Luồng trong ASL

{% hint style="info" %}
ASL có các phương pháp để kiểm soát luồng tương tự như trong các ngôn ngữ lập trình khác

> Bao gồm:
>
> * Switch
>   * Case
>   * Default
>   * BreakPoint
> * While
>   * Break
>   * Continue
> * If
>   * Else
>   * ElseIf
> * Stall
>   {% endhint %}

#### Điều Khiển Rẽ Nhánh If & Switch

{% hint style="info" %}
Giới thiệu về IF:
{% endhint %}

{% hint style="success" %}
Mã sau kiểm tra xem hệ thống có phải là Darwin không, nếu đúng thì gán `OSYS = 0x2710`.
{% endhint %}

```asl
If (_OSI ("Darwin"))
{
    OSYS = 0x2710
}
```

{% hint style="info" %}
**Giới thiệu ElseIf và Else**
{% endhint %}

{% hint style="success" %}
Mã sau kiểm tra xem hệ thống có phải là Darwin không

Nếu không phải thì kiểm tra hệ thống có phải là Linux không

Nếu đúng thì gán `OSYS = 0x03E8`

Nếu không thì gán `OSYS = 0x07D0`.
{% endhint %}

```asl
If (_OSI ("Darwin"))
{
    OSYS = 0x2710
}
ElseIf (_OSI ("Linux"))
{
    OSYS = 0x03E8
}
Else
{
    OSYS = 0x07D0
}
```

{% hint style="info" %}
**Giới thiệu về Switch, Case, Default, BreakPoint**
{% endhint %}

{% hint style="success" %}
Ví dụ sau sử dụng cấu trúc `Switch`, `Case`, `Default`, và `BreakPoint` để kiểm soát luồng.
{% endhint %}

```asl
Switch (Arg2)
{
    Case (1) /* Điều kiện 1 */
    {
        If (Arg1 == 1)
        {
            Return (1)
        }
        BreakPoint /* Điều kiện không khớp, thoát */
    }
    Case (2) /* Điều kiện 2 */
    {
        ...
        Return (2)
    }
    Default /* nếu không khớp điều kiện nào */
    {
        BreakPoint
    }
}
```

#### Kiểm Soát Vòng Lặp

{% hint style="info" %}
**Giới thiệu về While & Stall**
{% endhint %}

{% hint style="success" %}
Ví dụ sau sử dụng `While` và `Stall` để kiểm soát vòng lặp.
{% endhint %}

```asl
Local0 = 10
While (Local0 >= 0x00)
{
    Local0--
    Stall (32)
}
```

{% hint style="success" %}

* `Local0 = 10`: Khởi tạo `Local0` bằng 10.
* `While (Local0 >= 0x00)`: Thực hiện vòng lặp khi `Local0` lớn hơn hoặc bằng 0.
* `Local0--`: Giảm giá trị của `Local0` đi 1.
* `Stall (32)`: Tạm dừng 32 microsecond.
  {% endhint %}

{% hint style="info" %}

#### Giới thiệu về Vòng Lặp For

{% endhint %}

{% hint style="success" %}
Cấu trúc `for` trong ASL tương tự như trong C, Java.
{% endhint %}

```asl
for (Local0 = 0, Local0 < 8, Local0++)
{
    ...
}
```

{% hint style="success" %}
Cấu trúc `For` trên tương đương với `While` dưới đây.
{% endhint %}

```asl
Local0 = 0
While (Local0 < 8)
{
    Local0++
}
```

{% hint style="info" %}

#### Giới thiệu về ASL CondRefOf

{% endhint %}

{% hint style="success" %}
`CondRefOf` hữu ích để kiểm tra xem đối tượng có tồn tại hay không.
{% endhint %}

```asl
Method (SSCN, 0, NotSerialized)
{
    If (_OSI ("Darwin"))
    {
        ...
    }
    ElseIf (CondRefOf (\_SB.PCI0.I2C0.XSCN))
    {
        If (USTP)
        {
            Return (\_SB.PCI0.I2C0.XSCN ())
        }
    }

    Return (Zero)
}
```

{% hint style="success" %}
Mã này được trích từ SSDT-I2CxConf.&#x20;

Khi hệ thống không phải là MacOS, và `XSCN` tồn tại dưới `I2C0`, nó trả về giá trị gốc.
{% endhint %}

## Trình tự tải SSDT

{% hint style="info" %}
Thông thường, các bản vá SSDT được nhắm vào ACPI của máy

> Có thể là DSDT hoặc các SSDT gốc máy khác

&#x20;Vì ACPI gốc được tải trước các bản vá SSDT

> Nên không cần phải tải các SSDT trong danh sách Add theo thứ tự cụ thể.&#x20;

Tuy nhiên, có những ngoại lệ đối với quy tắc này.&#x20;

> Ví dụ, nếu bạn có hai SSDT-X và SSDT-Y
>
> Trong đó SSDT-X định nghĩa một thiết bị mà SSDT-Y tham chiếu chéo thông qua Scope
>
> > Thì hai bản vá này phải được tải theo đúng thứ tự để toàn bộ bản vá hoạt động.&#x20;
> >
> > > Nói chung, các SSDT được "scoped" vào phải được tải trước các SSDT khác.
> > > {% endhint %}

### Ví dụ

#### SSDT-X

```
External (_SB.PCI0.LPCB, DeviceObj)
Scope (_SB.PCI0.LPCB)
{
    Device (XXXX)
    {
        Name (_HID, EisaId ("ABC1111"))
    }
}
```

{% hint style="info" %}
SSDT-X: Định nghĩa một thiết bị `XXXX` dưới Scope `_SB.PCI0.LPCB`.
{% endhint %}

#### SSDT-Y

```
External (_SB.PCI0.LPCB.XXXX, DeviceObj)
Scope (_SB.PCI0.LPCB.XXXX)
{
    Method (YYYY, 0, NotSerialized)
    {
        /* do nothing */
    }
}

```

{% hint style="info" %}
SSDT-Y: Tham chiếu thiết bị `XXXX` được định nghĩa trong `SSDT-X` và định nghĩa một Method `YYYY`.
{% endhint %}

{% hint style="success" %}
Trong ví dụ này SSDT-X phải được load trước SSDT-Y
{% endhint %}

### Thiết lập trình tự tải cho SSDT

{% hint style="info" %}
Đảm bảo các file SSDT của bạn đã được load vào config.plist
{% endhint %}

B1: Mở Config.plist bằng [Propertree](https://github.com/corpnewt/ProperTree)

<figure><img src="/files/VaVHZU9J95pbL053sljw" alt=""><figcaption></figcaption></figure>

{% hint style="info" %}
Chú ý vào phần ACPI --> ADD&#x20;

> Như hình trên thì SSDT-PLUG-DRTNIA sẽ được load trước SSDT-EC-USBX-DESKTOP
> {% endhint %}

B2: Nếu bạn muốn thay đổi thứ tự load của SSDT nào chỉ cần kéo "dòng" của SSDT đó lên trước

<figure><img src="/files/ii6lXeBH8fgxxkR6CX6m" alt=""><figcaption></figcaption></figure>

{% hint style="info" %}
Như này thì SSDT-EC-USBX-DESKTOP đã load trước SSDT-PLUG-DRTNIA
{% endhint %}

{% hint style="warning" %}
Source tham khảo: <https://github.com/5T33Z0/OC-Little-Translated/blob/main/00_ACPI/ACPI_Basics/ASL_Basics.md>
{% endhint %}


---

# Agent Instructions: Querying This Documentation

If you need additional information that is not directly available in this page, you can query the documentation dynamically by asking a question.

Perform an HTTP GET request on the current page URL with the `ask` query parameter:

```
GET https://advance.heavietnam.com/acpi-advance/tim-hieu-ve-asl.md?ask=<question>
```

The question should be specific, self-contained, and written in natural language.
The response will contain a direct answer to the question and relevant excerpts and sources from the documentation.

Use this mechanism when the answer is not explicitly present in the current page, you need clarification or additional context, or you want to retrieve related documentation sections.
