Intel 8080 汇编语法解析:基于 ANTLR4 的 asm8080 文法解析 CP/M 源码实战
【免费下载链接】grammars-v4Grammars written for ANTLR v4; expectation that the grammars are free of actions.项目地址: https://gitcode.com/gh_mirrors/gr/grammars-v4
导读
本文围绕 grammars-v4 仓库中的 asm8080 ANTLR4 文法展开,该文法专为解析 Intel 8080 汇编文件而设计,其创建初衷是解析 CP/M 操作系统的汇编源代码。读完本文,你将掌握:asm8080 文法的整体结构与语法规则、表达式求值与操作数解析机制、伪指令与宏指令覆盖范围、在 Maven 工程中生成解析器并运行测试的完整流程,以及如何基于该文法构建自己的 8080 汇编解析工具。
一、项目背景与目标
1.1 文法定位
asm8080.g4 是 grammars-v4 仓库中针对 Intel 8080 微处理器汇编语言编写的 ANTLR4 文法。与仓库中其他汇编文法(如 asm8086、asmZ80、asm6502)同属于 asm 目录,每个文法子目录均遵循"文法文件 + desc.xml + pom.xml + examples"的统一结构。
从文法头部注释可以看出其设计动机:
Created for the purpose of parsing the CP/M Source code
即该文法的首要目标是解析 CP/M 操作系统(2.2 与 3.0 版本)的官方汇编源码,这类源码采用 Digital Research 风格的汇编器语法,包含大量宏、条件汇编、表达式求值等特性,对解析器有较高要求。
1.2 关键设计决策:caseInsensitive
文法在options块中声明了大小写不敏感模式:
options { caseInsensitive = true; }这符合 8080 汇编的书写惯例——CP/M 时代的源码中,指令助记符、寄存器名、标签、伪指令混合使用大写和小写(例如MOV与mov、EQU与equ均合法),大小写不敏感选项保证了两者都能被正确识别。
二、文法整体结构解析
2.1 顶层规则与行的组成
prog : EOL* ((line EOL+)* line EOL*)? EOF ; line : lbl? (instruction | directive | macrocall) ('!' (instruction | directive | macrocall)?)* comment? | lbl comment? | comment ;顶层规则prog允许空文件(仅由换行符EOL和EOF组成),也允许任意数量的line。line规则反映了 8080 汇编源代码行的典型结构:
- 可选的标签
lbl(标签后冒号:可选,兼容LABEL:与LABEL两种写法); - 一条主体语句,可以是机器指令
instruction、伪指令directive或宏调用macrocall; - 可选的
!分隔符用于在同一行内连接多条语句(这是 CP/M 汇编器支持的紧凑写法); - 可选的注释
comment。
2.2 语句分类:指令 / 伪指令 / 宏调用
instruction : opcode expressionlist? ; macrocall : name expressionlist? ; directive : argument? assemblerdirective expressionlist? ;三类语句共享"助记符 + 操作数列表"的基本形态,操作数列表由逗号分隔:
expressionlist : expression (',' expression)* ;instruction的操作数位置是OPCODE词法记号(见 2.4);macrocall则把第一个记号解析为普通标识符name,这是宏调用与指令在语法层面上的关键区分——凡是OPCODE词法表中未收录的助记符,都被视为宏名。directive中伪指令之前还可以出现可选参数argument(例如条件汇编IF后接条件表达式)。
三、表达式求值体系:从操作数到完整运算符栈
asm8080 文法最值得称道的部分是它完整实现了 8080 汇编操作数的表达式求值。expression规则构建了一个覆盖全部优先级层次的表达式语法树:
expression : orExpression (('=' | '<>' | '<' | '>' | '<=' | '>=' | OP_GT | OP_LT | OP_GE | OP_LE | OP_NE | OP_EQ) orExpression)? ; orExpression : xorExpression (OP_OR xorExpression)* ; xorExpression : andExpression (OP_XOR andExpression)* ; andExpression : addExpression (OP_AND addExpression)* ; addExpression : shiftExpression (('+' | '-') shiftExpression)* ; shiftExpression : modExpression ((OP_SHL | OP_SHR) modExpression)* ; multiplyingExpression : unaryExpression (('*' | '/') unaryExpression)* ; unaryExpression : OP_NOT unaryExpression | OP_HIGH unaryExpression | OP_LOW unaryExpression | '-' unaryExpression | '+' unaryExpression | argument ;3.1 运算符优先级层次
从低到高依次为:比较运算(=<><><=>=及关键字形式EQNEGTLTGELE)→ 逻辑或OR→ 逻辑异或XOR→ 逻辑与AND→ 加减+-→ 移位SHLSHR→ 乘除*/→ 一元运算NOTHIGHLOW与正负号。
以 CP/M 3 引导加载器源码 OS1BOOT.ASM 中的实际用法为例:
true equ not false testing equ false ;if true, then go to mon80 on errors bdos equ 806h+bias ;entry to dos for calls bdosl equ bdose-cpmb bdoss equ bdosl/128 ;number of sectors in dos bdos0 equ 25 ;number of bdos sectors on track 0 bdos1 equ bdoss-bdos0 ;number of sectors on track 1这些表达式涵盖了一元NOT、加减、除法以及符号引用,均可被上述规则链完整解析。
3.2 一元运算符 HIGH / LOW / NOT
HIGH和LOW是 8080 汇编中常用的取字节操作(分别取 16 位地址的高、低字节),NOT为逻辑取反。它们在文法中作为一元前缀运算符递归出现,可以叠加使用。
3.3 操作数原子的种类
argument规则定义了操作数的全部原子形态:
argument : number | opcode | register_ | dollar | name | string_ | '(' expression ')' ;number:数值字面量(见 4.4);opcode/register_:指令助记符和寄存器名本身也可作为操作数(例如DB指令中存放MOV等指令字节码的场景很常见);dollar:$表示当前地址计数器(location counter),如 CPM22.ASM 中ENDMOD EQU ($ AND 0FF00H)+100H的用法;name:符号引用(标签、EQU 定义的常量等);string_:字符串字面量;'(' expression ')':括号表达式,支持任意嵌套。
四、词法规则深度剖析
4.1 伪指令表 ASSEMBLER_DIRECTIVE
ASSEMBLER_DIRECTIVE : 'ORG' | 'END' | 'EQU' | 'DB' | 'DW' | 'DS' | 'IF' | 'ELSE' | 'ENDIF' | 'SET' | 'TITLE' | '$TITLE' | 'NAME' | 'CSEG' | 'DSEG' | 'ASEG' | 'PUBLIC' | 'EXTRN' | 'MACRO' | 'ENDM' | 'EXITM' | 'REPT' | 'MACLIB' ;该表覆盖了三类伪指令:
- 数据与定位:
ORG(设置起始地址)、END(源文件结束)、EQU(常量定义)、DB/DW(定义字节/字)、DS(保留存储区)、SET(可重定义常量); - 条件汇编与列表控制:
IF/ELSE/ENDIF(条件汇编块)、TITLE/$TITLE(页面标题)、NAME(模块名); - 模块化与宏:
CSEG/DSEG/ASEG(代码段/数据段/绝对段)、PUBLIC/EXTRN(符号导出与外部引用)、MACRO/ENDM/EXITM(宏定义与退出)、REPT(重复块)、MACLIB(宏库引入)。
在 CPM22.ASM 中,EQU、ORG、DB、DW被大量使用,例如:
MEM EQU 62 ;for a 62k system (TS802 TEST - WORKS OK). IOBYTE EQU 3 ;i/o definition byte. ORG (MEM-7)*1024 INBUFF DB 127 ;length of input buffer. INPOINT DW INBUFF+2;input line pointer注意这里EQU右侧的(MEM-7)*1024恰好印证了括号表达式与乘法的解析能力。
4.2 表达式运算符关键字:先于 NAME 声明
OP_NOT : 'NOT' ; OP_HIGH: 'HIGH'; OP_LOW : 'LOW' ; OP_AND : 'AND' ; OP_OR : 'OR' ; OP_XOR : 'XOR' ; OP_SHL : 'SHL' ; OP_SHR : 'SHR' ; OP_MOD : 'MOD' ; OP_GT : 'GT' ; OP_LT : 'LT' ; OP_GE : 'GE' ; OP_LE : 'LE' ; OP_NE : 'NE' ; OP_EQ : 'EQ' ;文法注释给出了这些关键字必须排在NAME之前的理由:由于 8080 指令集中存在SHLD、ORI、ANA这类以SHL/OR/AND开头的助记符,ANTLR 的词法最长匹配(longest-match)机制仍能保证SHLD整体被识别为OPCODE,而不会被拆成SHL+D。关键字规则前置是为了让独立的NOT、SHL等记号优先于NAME被识别。
4.3 寄存器表 REGISTER
REGISTER : 'A' | 'B' | 'C' | 'D' | 'E' | 'H' | 'L' | 'PC' | 'SP' ;覆盖 8080 的 8 位通用寄存器(A、B、C、D、E、H、L)以及 16 位专用寄存器 PC(程序计数器)与 SP(栈指针)。由于caseInsensitive = true,小写形式同样有效。
4.4 助记符表 OPCODE
OPCODE规则完整收录了 Intel 8080 的全部指令集,按功能可分为:
- 数据传输:
MOV、MVI、LDA、STA、LDAX、STAX、LHLD、SHLD、LXI、PUSH、POP、XTHL、SPHL、PCHL、XCHG; - 算术与逻辑:
ADD、ADC、SUB、SBB、INR、DCR、CMP、ANA、ORA、XRA、DAD、DAA以及立即数形式ADI、ACI、SUI、SBI、CPI、ANI、ORI、XRI; - 跳转/调用/返回:无条件
JMP/CALL/RET,以及全部条件形式JNZ/JZ/JNC/JC/JPO/JPE/JP/JM、CNZ/CZ/CNC/CC/CPO/CPE/CP/CM、RNZ/RZ/RNC/RC/RPO/RPE/RP/RM; - 栈与机器控制:
RST、IN、OUT、EI、DI、HLT、NOP、CMC、STC、CMA、RAL、RAR、RLC、RRC。
4.5 标识符与数值字面量
NAME : [A-Z?@] [A-Z0-9.$?@"]* ; NUMBER : '$'? [0-9A-F] [0-9A-F$]* 'H'? ;NAME允许以字母、?(PL/I 风格外部符号,如?bdos)或@开头;$允许出现在名称内部(如sta$ret、rsx$chain这类 CP/M 内部符号)但不能作为首字符,从而与表示当前地址的独立$记号区分开来。这在文法注释中有明确说明。NUMBER的形态直接来自 8080 汇编的数值书写惯例:十六进制数以H结尾(如5CH、0FFH),也可以$前缀开头;十进制、二进制(如0101B)等书写形式同样落入该规则。注意 CPM22.ASM 中DB 0,0,0,...与DB 'Copyright'混用的场景,分别命中NUMBER与STRING。
4.6 注释、字符串、空白与其他
COMMENT : ';' ~ [\r\n]* | '*' '*'+ ~ [\r\n]* ; STRING : '\u0027' ('\u0027\u0027' | ~'\u0027')* '\u0027' ; EOL : [\r\n]+ ; WS : [ \t] -> skip ; ARTIFACT : [\u0000-\u0008\u000b\u000c\u000e-\u001f\u007f-\uffff] -> skip ;COMMENT支持两种注释前缀:分号;与星号*(CP/M 汇编器兼容形式,如** comment)。*后要求至少两个*,避免与乘号*冲突——这是通过'*' '*'+的写法实现的;STRING以单引号'\u0027'定界,内部使用双单引号''转义表示字面单引号(对应 CP/M 源码中常见的DB 'it''s'写法),同时允许单引号内直接出现$等字符;WS跳过空格与制表符;ARTIFACT规则负责丢弃源码中可能存在的控制字符与高位字符等二进制伪影,保证解析器对老旧源码的鲁棒性。
五、标签解析:允许标签与指令、寄存器重名
lbl : label ':'? ; label : name | register_ | assemblerdirective ;label规则值得注意:它不仅能由普通NAME构成,还允许标签与寄存器名(REGISTER)或伪指令名(ASSEMBLER_DIRECTIVE)同名。这正是解析真实 8080 汇编源码的现实需求——例如 CP/M 源码中大量存在SET、ENDMOD这样的标签,其中SET同时是伪指令关键字(见 AS2SCAN.ASM 中SETT EQU 0101B ;SET的用法,以及SET0:、SETUP:等标签),文法通过把label与directive作为line的并列分支来解决这种歧义。
六、工程配置与测试
6.1 Maven 构建配置
asm8080/pom.xml 声明了该文法模块的完整构建与测试方案:
- 使用
antlr4-maven-plugin从asm8080.g4生成解析器代码,同时开启 visitor 与 listener 两种遍历模式(<visitor>true</visitor>、<listener>true</listener>); - 使用
antlr4test-maven-plugin进行自动化测试,入口规则为prog,测试文件扩展名为.ASM,测试目录为examples/。
6.2 测试样例集
examples 目录包含两组真实世界测试语料:
- CPM22.ASM:CP/M 2.2 操作系统完整源码(约 3700 行),涵盖 EQU 常量定义、ORG 定位、DB/DW 数据定义、子程序注释风格等全部语法要素;
- cpm3_src:CP/M 3.0(Plus)源码,包含
AS0COM.ASM~AS6MAIN.ASM等汇编器自身模块、BIOS/BDOS/CCP 相关模块(如OS1BOOT.ASM、OS2CCP.ASM、OS3BDOS.ASM、OS4BIOS.ASM)以及CPM3ASM1.SUB等 SUBMIT 批处理文件。
这些样例验证了文法对真实历史代码的解析能力——尤其是TITLE '...'(见 AS0COM.ASM 首行)、条件汇编IF/ENDIF(见 OS1BOOT.ASM 中if testing ... endif结构)、嵌套表达式(ENDMOD EQU ($ AND 0FF00H)+100H)等特性。
6.3 支持的生成目标
desc.xml 声明了该文法支持的目标语言:CSharp、Cpp、Dart、Go、Java、JavaScript、PHP、Python3、TypeScript 以及 Antlr4ng,ANTLR 版本要求为^4.10。这意味着通过 ANTLR 工具链,同一份文法可以在上述任意目标语言中生成 8080 汇编解析器。
6.4 运行测试
在仓库根目录执行 Maven 测试命令即可验证文法:
mvn -pl asm/asm8080 testantlr4test 插件会以prog为入口规则逐个解析examples/下所有.ASM文件,任何无法解析的源码都会导致测试失败。
七、基于该文法的二次开发指引
7.1 生成解析器
确保已安装 ANTLR4 工具(版本 ≥ 4.10),然后执行:
antlr4 -visitor -listener asm8080.g4生成 Java 解析器后,即可在自定义程序中加载asm8080Lexer/asm8080Parser。由于文法不含任何 action(符合 grammars-v4 仓库"文法中不含动作"的约定),生成的代码是纯粹的数据驱动解析器,便于在任意目标语言中复用。
7.2 解析器骨架示例
import org.antlr.v4.runtime.*; import org.antlr.v4.runtime.tree.*; CharStream input = CharStreams.fromFileName("CPM22.ASM"); asm8080Lexer lexer = new asm8080Lexer(input); CommonTokenStream tokens = new CommonTokenStream(lexer); asm8080Parser parser = new asm8080Parser(tokens); ParseTree tree = parser.prog(); // 入口规则 ParseTreeWalker.DEFAULT.walk(new asm8080BaseListener(), tree);基于生成的asm8080BaseVisitor/asm8080BaseListener,可以进一步实现符号表收集(遍历EQU/label)、代码重定位(解析ORG与$)、宏展开等工具链功能。表达式规则链(expression→unaryExpression→argument)为在 visitor 中实现操作数求值提供了天然的结构支撑。
八、总结
asm8080 文法的价值在于它以真实历史源码(CP/M 2.2 与 3.0)为测试基准,完整覆盖了 Intel 8080 指令集、Digital Research 汇编器风格的伪指令与宏语法、以及全套表达式运算符。无论是研究古董操作系统源码、开发 8080 交叉汇编工具,还是学习如何为"语法复杂、惯例繁多"的汇编语言设计 ANTLR4 文法,这份文法都是极具参考价值的起点——其 README.md、文法源文件 与 测试样例 均可直接在仓库中查阅与复用。
【免费下载链接】grammars-v4Grammars written for ANTLR v4; expectation that the grammars are free of actions.项目地址: https://gitcode.com/gh_mirrors/gr/grammars-v4
创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考