- 编程语言
- 编译器
- 开发工具
【免费下载链接】grammars-v4
Grammars written for ANTLR v4; expectation that the grammars are free of actions.
导读
本文以 grammars-v4 仓库中的 asm/asm8086/README.md 及其核心文法文件 asm8086.g4 为主体,系统讲解如何用 ANTLR4 为 Intel 8086 汇编语言建立词法与语法模型,并以 CP/M-86 官方源码(BIOS、CBIOS、TRACK 等.A86文件)作为真实解析目标。读完本文,你将掌握该文法的整体结构、行级语法、汇编伪指令、表达式体系、寄存器与指令助记符覆盖范围、词法规则要点,以及如何在本仓库环境中对其实例源码执行构建与测试,为后续基于解析树实现反汇编、静态分析或源码检索工具打下基础。
一、项目背景与文法定位
Intel 8086 是 x86 指令集的开端,其汇编语言既被当年 CP/M-86、早期 DOS 系统广泛使用,也是理解现代 x86 工具链的重要历史标本。该仓库将这套汇编语法沉淀为一个无 action 的纯 ANTLR4 文法(符合 grammars-v4 仓库"期望文法不包含 action"的整体规范),存放于 asm/asm8086/。
目录核心文件如下:
- asm8086.g4:词法 + 语法合一的 ANTLR4 文法(1112 行),覆盖 CP/M-86 汇编器所使用的 8086 指令集与汇编伪指令;
- README.md:文法用途与来源说明;
- examples/:9 个真实 CP/M-86 汇编源文件,作为解析与测试样例;
- pom.xml:Maven 构建与自动测试配置;
- desc.xml:文法元信息(ANTLR 版本要求与目标语言)。
从文法创建目的看,该文法是为解析 CP/M 操作系统源码而设计(见 asm/asm8086/README.md),其语法形态因此带有明显的 8086 时代汇编器风格:使用ORG/EQU/DB/DW/CSEG/DSEG/RB/RW/RS等传统伪指令,支持IF/ENDIF条件汇编,并允许一行内用!分隔多条指令。在仓库体系中,它隶属于 asm/ 聚合模块,与 asm6502、asm8080、asmZ80、nasm、masm 等汇编文法并列(见 asm/pom.xml),可作为"同一种 x86/其他处理器汇编文法横向对比"的参考实现。
二、文法总体骨架:prog → line
文法入口与行结构非常简洁,见 asm8086.g4:
grammar asm8086; options { caseInsensitive = true; } prog : line* EOF ; line : lbl? (assemblerdirective | instruction)? ('!' instruction)* EOL ; instruction : rep? opcode expressionlist? ;三个要点:
caseInsensitive = true(asm8086.g4):整个文法大小写不敏感,因此MOV、mov、Mov均可被识别。这是 8086 汇编历史惯例的体现——传统汇编器普遍不区分大小写;prog由若干line组成并以EOF收尾,解析入口即prog;line是基本单元:可选标签(lbl?)+ 可选的汇编伪指令或指令((assemblerdirective | instruction)?)+ 可选的!后续指令序列 + 行结束符EOL。
三、行级语法:标签、指令与!多语句分隔符
3.1 标签(lbl)
lbl : label COLON? ;标签可以带冒号也可以不带(COLON?),这贴合 8086 时代汇编器的习惯:init:、old_disk:与裸标签均合法。在 examples/TRACK.A86 中可以看到大量此类用法,例如:
init: call clear_flags ; Initialize track buffering . . . jmp CCP_entry3.2 一行内的多条指令:!分隔
line规则中的('!' instruction)*是这套 8086 汇编风格最鲜明的特征:允许用!在同一行书写多条指令。例如 examples/TRACK.A86 中:
old_disk: mov bl,cpm_disk ! mov bh,0 mov dl,4 ! shl bx,dl ; times 16以及:
mov bx,ds ! mov es,bx ; destination is our data segment mov cx,host_spt ! cld ; set length and direction这种写法用于节省行数、提高列表文件密度,是 CP/M 汇编源码的常见风格,文法对此做了专门支持。
3.3 指令(instruction)
instruction : rep? opcode expressionlist? ;- 可选的前缀
rep(REP/REPE/REPNE/REPNZ/REPZ,见 asm8086.g4)用于串操作指令; - 后接
opcode与可选的expressionlist(逗号分隔的表达式列表,见 asm8086.g4),例如mov si,offset track_buffer、div cx。
四、汇编伪指令(Assembler Directives)全解
assemblerdirective规则(asm8086.g4)收录了 CP/M 汇编器的核心伪指令,逐一拆解如下:
| 伪指令 | 文法规则 | 语义 | 示例(取自 examples) |
|---|---|---|---|
ORG | ORG expression | 设置后续代码/数据的起始地址 | bios_code equ 2500h(配合 EQU) |
END | END | 标记汇编结束 | 源码末尾 |
EQU | name EQU expression | 符号常量定义 | host_sectsiz equ 1024 |
DB/DW/DD | DB/DW/DD expressionlist | 定义字节 / 字 / 双字数据 | bdos_int equ 224(配合 EQU 定义) |
RB/RW/RS | name? RB/RW/RS expression | 保留字节 / 字 / 字符串存储区 | cpm_disk rb 1、cpm_track rw 1 |
CSEG/DSEG | CSEG/DSEG expression? | 声明代码段 / 数据段 | dseg |
IF/ENDIF | IF assemblerexpression/ENDIF | 条件汇编 | IF not loader_bios...ENDIF |
TITLE | TITLE string_ | 设置列表文件标题 | title '8086 Disk I/O Drivers' |
INCLUDE | INCLUDE name | 包含另一个源文件 | INCLUDE equates.inc |
. | DOT | 当前地址 / 其它点号起始的伪指令 | 列表输出中常见 |
4.1 存储区保留指令 RB / RW / RS
RB、RW、RS是这套文法相对独特的伪指令(对应 CP/M 汇编器的 "Reserve Byte / Word / String"),其表达式可以是任意算术表达式。在 examples/TRACK.A86 的数据段末尾,它们被用来按需动态计算缓冲区大小:
dseg cpm_disk rb 1 cpm_track rw 1 cpm_sec rw 1 dma_offset rw 1 dma_segment rw 1 dma_longword equ dword ptr dma_offset ... sec_flags rb host_spt ; bit 0 of each byte on indicates ; corresponding host sector has been ; updated and needs writing. track_buffer rb host_sectsiz * host_spt注意track_buffer rb host_sectsiz * host_spt:rb的表达式可以引用前面EQU定义的符号并做乘法运算,这要求表达式解析必须支持完整的算术求值(见下一节)。
4.2 条件汇编 IF / ENDIF
IF后面接的是专门的条件汇编表达式:
if_ : IF assemblerexpression ; assemblerexpression : assemblerterm (assemblerlogical assemblerterm)* | RP assemblerexpression LP ; assemblerlogical : EQ | NE ; assemblerterm : name | number | NOT assemblerterm ;即IF表达式支持EQ/NE比较、NOT取反以及括号分组。例如 examples/BIOS.A86 中的典型写法:
IF not loader_bios ;--------------------------------------------- bios_code equ 2500h ccp_offset equ 0000h bdos_ofst equ 0B06h ;BDOS entry point ;--------------------------------------------- ENDIF ;not loader_bios IF loader_bios ;--------------------------------------------- bios_code equ 1200h ;start of LDBIOS ccp_offset equ 0003h ;base of CPMLOADER bdos_ofst equ 0406h ;stripped BDOS entry ;--------------------------------------------- ENDIF ;loader_bios以及 examples/TRACK.A86 中基于常量判断的:
if (host_fsn ne 0) sub ax,host_fsn ; correct if we start with sector one endif五、表达式系统:从算术运算到寻址模式
表达式的递归结构定义在 asm8086.g4:
expression : multiplyingExpression (sign multiplyingExpression)* ; multiplyingExpression : argument ((STAR | SLASH | MOD | AND) argument)* ; argument : number | dollar | register_ | name | string_ | RP expression LP | (number | name)? LB expression RB_ | ptr expression | NOT expression | OFFSET expression | LENGTH expression | register_ COLON expression ; ptr : (BYTE | WORD | DWORD)? PTR ;5.1 运算符优先级
- 优先级最低的是加减(
sign,即+/-); - 更高一层是乘除与模/位与:
*、/、MOD、AND; - 一元运算
NOT、OFFSET、LENGTH作用在表达式上; - 括号
( ... )可任意嵌套。
例如 examples/TRACK.A86 中的mov dl,4 ! shl bx,dl ; times 16以及add si,ax、mov cx,host_sectsiz等,均落在上述表达式的覆盖范围内。
5.2 特殊操作数
argument覆盖了 8086 汇编中最常见的操作数形态:
number:立即数(词法细节见第七节);dollar:$,即当前位置计数器(asm8086.g4);register_:寄存器直接寻址;name:符号(标号或变量名);string_:单引号字符串(如title '8086 Disk I/O Drivers');(number | name)? LB expression RB_:带基址/变址的存储器寻址,例如sec_flags[BX]、mov bl,[BX];ptr expression:类型强制转换,如dword ptr dma_offset(examples/TRACK.A86),支持BYTE/WORD/DWORD三种前缀;OFFSET expression:取偏移地址,如mov si,offset track_buffer;LENGTH expression:取长度;register_ COLON expression:段覆盖前缀,如les di,dma_longword隐含的 ES:DI 语义在源码中体现为mov bl,[BX]这类带段寄存器的书写。
六、寄存器与指令助记符覆盖
6.1 寄存器集合
register_规则(asm8086.g4)覆盖了 8086 的全部常用寄存器:
- 8 位:
AH AL BH BL CH CL DH DL; - 16 位通用:
AX BX CX DX、CI DI BP SP; - 段寄存器与指令指针:
CS DS ES SS、IP。
一个值得注意的实现细节:文法将源寄存器SI建模为词法记号CI(词法规则见 asm8086.g4,规则文本为'CI' : 'CI')。由于整条文法开启了caseInsensitive,'CI'记号可以匹配源码中的si/SI(例如 examples/TRACK.A86 中的lds si,dma_longword)。从源码结构看,这应是历史命名(C 源寄存器 / 某种方言)留下的记号名,使用时只需知道解析树中的CI记号对应SI寄存器即可。
6.2 指令助记符
opcode规则(asm8086.g4)逐一列举了 8086 指令集的助记符,按功能可归纳为:
- 数据传送:
MOV、PUSH、POP、PUSHF、POPF、XCHG、XLAT、LEA、LDS、LES、IN、OUT、LAHF、SAHF; - 算术运算:
ADD、ADC、SUB、SBB、INC、DEC、NEG、CMP、MUL、IMUL、DIV、IDIV、DAA、DAS、AAA、AAS、AAM、AAD、CBW、CWD; - 逻辑与移位:
AND、OR、XOR、NOT、TEST、SHL、SHR、SAL、SAR、ROL、ROR、RCL、RCR; - 串操作:
MOVS、MOVSB、MOVSW、CMPS(CMPSB/CMPSW)、SCASB/SCASW、LODSB/LODSW、STOSB/STOSW; - 控制转移:
JMP(含JMPF/JMPS)、CALL、RET/RETN/RETF、全部条件跳转JA/JAE/JB/JBE/JC/JE/JG/JGE/JL/JLE/JNA/JNAE/JNB/JNBE/JNC/JNE/JNG/JNGE/JNL/JNLE/JNO/JNP/JNS/JNZ/JO/JP/JPE/JPO/JS/JZ/JCXZ、循环指令LOOP/LOOPE/LOOPNE/LOOPNZ/LOOPZ; - 标志与处理器控制:
CLC、CLD、CLI、CMC、STC、STD、STI、HLT、NOP、WAIT、ESC、LOCK、IRET、INTO、INT; - 扩展/保留:
SALC等少见助记符也被收录。
配合rep前缀(REP/REPE/REPNE/REPNZ/REPZ),典型串操作如rep movsw(examples/TRACK.A86 的 read/write 例程)可以被完整解析。
七、词法规则要点
词法规则定义了指令、符号、数字与注释的精确外形,见 asm8086.g4:
NAME : [.A-Z] [A-Z0-9."_]* ; NUMBER : [0-9A-F]+ 'H'? ; STRING : '\u0027' ~'\u0027'* '\u0027' ; COMMENT : ';' ~ [\r\n]* -> skip ; EOL : [\r\n]+ ; WS : [ \t] -> skip ;- NUMBER:
[0-9A-F]+ 'H'?是这套文法的特色——它天然支持十六进制数(可带H后缀,如0DAh、0B06h、2500h,大小写不敏感下h亦可),同时也接受纯十进制数字串(不带H时按十进制理解)。这与 8086 汇编"十六进制必须带 H 后缀"的书写惯例完全一致; - NAME:标识符以点号或字母开头,可包含字母、数字、点号、下划线与双引号,因此
dma_longword、track_buffer、host_sectsiz * host_spt中的符号均可识别; - STRING:单引号字符串,用于
TITLE等伪指令; - COMMENT:
;到行尾为注释并直接跳过(-> skip),例如 examples 中大量; save the selected drive注释; - EOL:
[\r\n]+作为行终结符,支持 CRLF 与 LF;WS仅跳过空格与制表符,换行不作为空白跳过,而是作为结构性的EOL——这是整个line规则得以成立的前提。
符号类词法记号还包括* / $ + - ! : . ( ) , ; [ ](STAR、SLASH、DOLLAR、PLUS、MINUS、NOT_、COLON、DOT、RP、LP、COMMA、SEMI、LB、RB_ 等,见 asm8086.g4),其中NOT_对应!字符,用于line中的多语句分隔与表达式取反。
八、真实示例解读:examples 目录与 CP/M-86 源码
examples/ 收录了 9 个来自 CP/M-86 源码发行版的真实.A86汇编文件,均是该文法的直接解析对象:
- BIOS.A86:面向 iSBC 86/12 单板机与 iSBC 204 软盘控制器的 CP/M-86 BIOS,包含大量
EQU常量、IF/ENDIF条件汇编与TITLE; - CBIOS.A86:定制版 BIOS;
- LDBIOS.A86 与 LDCPM.A86、LDCOPY.A86:加载器 BIOS 相关例程;
- COPYDISK.A86:磁盘复制工具;
- ROM.A86 与 TBIOS.A86:ROM 化与终端 BIOS;
- TRACK.A86:轨道缓冲(Track Buffering)例程,是理解本文法全部特性的最佳样例——它同时用到了
equ、dseg、rb/rw、db、if/endif、!多语句、rep movsw、offset、dword ptr、[BX]索引寻址、段覆盖等全部核心语法要素。
以 TRACK.A86 的flush_buffer例程为例,可以看到文法与真实源码的完整对应:
flush_buffer: test wr_flag,1 ; see if we have anything to write jz no_flush ; no, skip scanning for dirty sectors mov bx,0 ; start at host sector 0 mov cx,host_spt ; for host_spt sectors... next_sect: test sec_flags[BX],1 ; see if this sector has been changed jz not_updated ; no, leave it alone mov sec_flags[BX],0 ; zero the flag for next time push bx ; save the registers push cx mov cur_sec,bx ; save host sector number mov ax,host_sectsiz mul bx ; make track buffer offset add ax,offset track_buffer ; make direct pointer mov cur_dma,ax ; save for write routine call sector_write pop cx pop bx not_updated: inc bx loop next_sect no_flush: mov wr_flag,0 ; clear the dirty buffer flag ret其中test sec_flags[BX],1对应(number | name)? LB expression RB_寻址,add ax,offset track_buffer对应OFFSET expression,jz/jnz/loop等全部落在opcode列表内,;注释被词法规则直接跳过。
九、构建、生成与自动化测试
9.1 Maven 构建配置
asm/asm8086/pom.xml 是标准的 grammars-v4 模块配置,使用antlr4-maven-plugin生成解析器,并开启 visitor 与 listener 两种遍历模式:
<plugin> <groupId>org.antlr</groupId> <artifactId>antlr4-maven-plugin</artifactId> <configuration> <sourceDirectory>${basedir}</sourceDirectory> <includes> <include>asm8086.g4</include> </includes> <visitor>true</visitor> <listener>true</listener> </configuration> </plugin>生成解析器后,即可用prog作为入口规则对任意 8086 汇编源文件构造解析树,并基于生成的Asm8086Parser/Asm8086Lexer编写 visitor 或 listener 实现语义分析。
9.2 自动测试:antlr4test-maven-plugin
同文件还集成了antlr4test-maven-plugin,对 examples/ 下的全部样例执行回归解析:
<plugin> <groupId>com.khubla.antlr</groupId> <artifactId>antlr4test-maven-plugin</artifactId> <configuration> <verbose>false</verbose> <showTree>false</showTree> <entryPoint>prog</entryPoint> <grammarName>asm8086</grammarName> <exampleFiles>examples/</exampleFiles> </configuration> </plugin>即在模块目录下执行mvn test,插件会以prog为入口规则逐一解析examples/中的 9 个.A86文件,任何语法不匹配都会导致测试失败。这意味着仓库自身就是该文法正确性的回归测试集。
9.3 版本与目标语言要求
desc.xml 声明:
- ANTLR 版本:
^4.10(即 4.10 及以上版本); - 目标语言:CSharp、Cpp、Dart、Go、Java、JavaScript、TypeScript、Antlr4ng、PHP、Python3。
因此,在满足 ANTLR 4.10+ 的前提下,同一文法可以生成上述任一目标语言的解析器,便于在各自技术栈中复用。
十、使用建议与已知实现细节
- 入口规则:解析任何 8086 汇编文件请以
prog为入口;构建工具链或静态分析程序时,优先遍历line节点以保持"一行一节点"的结构化视图; - 大小写不敏感:由于
caseInsensitive = true,输出的记号名统一为大写,词法错误提示也以此为准; SI寄存器的记号名是CI:在 visitor/listener 中处理寄存器时应留意这一历史遗留命名(见第六节);!分隔:一行内多个指令在解析树中体现为同一line节点下的多个instruction子节点,遍历时勿遗漏('!' instruction)*部分;- 表达式求值:伪指令(
RB/RW/RS/DB/DW/DD/ORG/EQU)的表达式可以是含符号引用的完整算术式(如track_buffer rb host_sectsiz * host_spt),如需计算实际布局,应在语义阶段维护符号表并对表达式树求值; - 局限:该文法为纯语法层实现,不含任何语义 action,也不做指令合法性/寄存器类型检查,这些需要在使用方(visitor/listener)中自行实现——这也正是 grammars-v4 仓库"grammars free of actions"设计哲学的体现。
结语
asm8086 文法以不到 1200 行的 ANTLR4 定义,完整承载了 Intel 8086 指令集、CP/M 汇编器伪指令体系与真实历史源码(CP/M-86 BIOS/TRACK 等)的全部语法要素,是研究 x86 汇编历史、构建复古系统解析工具或学习 ANTLR4 文法设计的优质范本。借助其内置的 Maven 测试配置,任何对该文法的修改都能立刻通过 9 个真实源码样例得到回归验证。若需进一步了解仓库整体文法生态,可参考 asm/readme.md 及根目录 README.md。
- 编程语言
- 编译器
- 开发工具
【免费下载链接】grammars-v4
Grammars written for ANTLR v4; expectation that the grammars are free of actions.
相关推荐
DIF(Data Interchange Format)语法解析:基于 ANTLR4 的 dif 文法实现与实战指南
DIF(Data Interchange Format)语法解析:基于 ANTLR4 的 dif 文法实现与实战指南 本文以 grammars v4 仓库中的
编程语言编译器开发工具基于 ANTLR4 的 FASTA 格式解析:fasta 语法详解与实战指南
基于 ANTLR4 的 FASTA 格式解析:fasta 语法详解与实战指南 导读 FASTA 是生物信息学领域最通用的序列存储格式之一,被 NCBI、Ense
编程语言编译器开发工具基于 ANTLR4 的 ALPACA 元胞自动机语言文法解析:grammars-v4/alpaca 语法实战指南
基于 ANTLR4 的 ALPACA 元胞自动机语言文法解析:grammars v4/alpaca 语法实战指南 导读 本文面向希望理解并复用 ALPACA 语
编程语言编译器开发工具
创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考