# 第1章：认识 XIRASM

## 同一份源文件，两种代码

XIRASM 源文件可以包含两种形式的代码：

- **机器指令**：就是你熟悉的汇编源码那些指令。
- **编译时代码**：编译期执行，比如计算值，生成数据，控制汇编流程，代码生成，复杂DSL等需求。

两种代码(前端语法和ISA汇编语法)写在同一语言里。

且计算出的值可以直接用在指令中，运行时接口可以引用标签，代码也可以同时产生数据和机器指令(静态数据生成)。

例子：

```asm
// 开启 64 位 x86 模式。
x86.use64();

// 编译时计算常量，指令里直接用。
const answer: u32 = 40 + 2

entry:
    // 把计算好的数值放进 eax，然后返回。实际上这就是硬编码需要的值而已。
    mov eax, answer
    ret
```

简单说明：

- `x86.use64();` 选择 64 位 x86 指令编码模式，这是后端编码器需要的，否则编码器无法正确编码。
- `const answer: u32 = 40 + 2` 声明编译时常量。
- `entry:` 在当前逻辑地址设一个标签。
- `mov eax, answer` 是普通 x86 指令，把 `answer` 移入 eax，编译时自动代入常量值。
- `ret` 是普通 x86 指令。

`x86.use64()` 这种调用式语法用于编译时操作，`mov eax, answer` 是自然汇编语法，不用写成函数调用。

## 运行一个例子

源码存为 `hello.xir(汇编文件后缀随意)`，执行：

```text
xirasm hello.xir -o hello.bin --target x86-64
```

输出字节：

```text
b8 2a 00 00 00 c3
```

前五个字节是 `mov eax, 42` 的编码，最后一个字节是 `ret`。标签不产生字节，只记录一个地址。

直接输出时，XIRASM 默认生成 flat binary，不带操作系统格式的那些文件头。后面章节会讲怎么通过格式接口生成 PE、COFF、ELF。

## 编译时代码处理数据

编译时代码在汇编过程中执行，不是在最终程序里运行。

比如循环写四个字节：

```asm
// 编译时循环四次，每次写一个字节。
const count: u8 = 4

for value in range(0, count) {
    db(value);
}
```

生成的字节：

```text
00 01 02 03
```

`range(0, count)` 生成从 0 到 count-1 的序列。`for` 在编译时执行，每次调 `db(value)` 写一个字节。

搞清楚这个区别：

| 做什么           | 怎么写           |
| ---------------- | ---------------- |
| 写机器指令       | 直接写汇编语法   |
| 计算和判断       | 编译时表达式     |
| 重复或条件生成   | 编译时流程控制   |
| 输出字节         | db、dw、dd、dq等 |
| 封装可复用逻辑   | 函数             |
| 生成 PE/COFF/ELF | 对应的格式接口   |

## 源文件语法注意点

几条简单规则：

- `//` 到行尾是单行注释，也可以写在 ISA 指令后；ISA 字符串操作数中的 `//` 仍是普通文本。
- 标签以 `:` 结尾。
- 函数和接口调用以 `;` 结尾。
- `return` 语句用 `;` 结尾。
- `const` 和 `let` 声明末尾**不加**分号。
- 代码块用 `{` `}`。
- 调用和返回值可以跨行，左括号会一直等到对应的右括号。

完整例子：

```asm
// 定义编译时可调用的辅助函数。
fn emit_marker(value: u8) {
    // 写固定标记，后面跟传入的值。
    db(0x7f, value);
}

const marker: u8 = 3
emit_marker(marker);

start:
    // 机器指令里直接用编译时定义的常量。但ISA指令后面别带分号; 后端不支持这个特性，暂时不支持。
    mov eax, marker
    ret
```

常量和函数是编译时逻辑，`mov`、`ret` 是机器码。写法不同，但写在同一份文件里。

## 怎么读 XIRASM 源码

读源码时，按这几步判断每行：

1. 如果是机器指令，就是汇编语法。
2. 如果需要编译时计算或生成数据，就是编译时语言。
3. 如果需要写字节或预留空间，就调用数据接口API(类似传统的db,rb,dd,rd等)。
4. 如果需要标准可执行文件格式，就要导入格式接口。

下一章讲编译时语言基础：值、常量绑定和赋值。

[返回语言指南](../language.md)
