Intel 8080 汇编语法解析:基于 ANTLR4 的 asm8080 文法解析 CP/M 源码实战
2026/9/24 22:36:16 网站建设 项目流程

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 时代的源码中,指令助记符、寄存器名、标签、伪指令混合使用大写和小写(例如MOVmovEQUequ均合法),大小写不敏感选项保证了两者都能被正确识别。

二、文法整体结构解析

2.1 顶层规则与行的组成

prog : EOL* ((line EOL+)* line EOL*)? EOF ; line : lbl? (instruction | directive | macrocall) ('!' (instruction | directive | macrocall)?)* comment? | lbl comment? | comment ;

顶层规则prog允许空文件(仅由换行符EOLEOF组成),也允许任意数量的lineline规则反映了 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

HIGHLOW是 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 中,EQUORGDBDW被大量使用,例如:

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 指令集中存在SHLDORIANA这类以SHL/OR/AND开头的助记符,ANTLR 的词法最长匹配(longest-match)机制仍能保证SHLD整体被识别为OPCODE,而不会被拆成SHL+D。关键字规则前置是为了让独立的NOTSHL等记号优先于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 的全部指令集,按功能可分为:

  • 数据传输MOVMVILDASTALDAXSTAXLHLDSHLDLXIPUSHPOPXTHLSPHLPCHLXCHG
  • 算术与逻辑ADDADCSUBSBBINRDCRCMPANAORAXRADADDAA以及立即数形式ADIACISUISBICPIANIORIXRI
  • 跳转/调用/返回:无条件JMP/CALL/RET,以及全部条件形式JNZ/JZ/JNC/JC/JPO/JPE/JP/JMCNZ/CZ/CNC/CC/CPO/CPE/CP/CMRNZ/RZ/RNC/RC/RPO/RPE/RP/RM
  • 栈与机器控制RSTINOUTEIDIHLTNOPCMCSTCCMARALRARRLCRRC

4.5 标识符与数值字面量

NAME : [A-Z?@] [A-Z0-9.$?@"]* ; NUMBER : '$'? [0-9A-F] [0-9A-F$]* 'H'? ;
  • NAME允许以字母、?(PL/I 风格外部符号,如?bdos)或@开头;$允许出现在名称内部(如sta$retrsx$chain这类 CP/M 内部符号)但不能作为首字符,从而与表示当前地址的独立$记号区分开来。这在文法注释中有明确说明。
  • NUMBER的形态直接来自 8080 汇编的数值书写惯例:十六进制数以H结尾(如5CH0FFH),也可以$前缀开头;十进制、二进制(如0101B)等书写形式同样落入该规则。注意 CPM22.ASM 中DB 0,0,0,...DB 'Copyright'混用的场景,分别命中NUMBERSTRING

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 源码中大量存在SETENDMOD这样的标签,其中SET同时是伪指令关键字(见 AS2SCAN.ASM 中SETT EQU 0101B ;SET的用法,以及SET0:SETUP:等标签),文法通过把labeldirective作为line的并列分支来解决这种歧义。

六、工程配置与测试

6.1 Maven 构建配置

asm8080/pom.xml 声明了该文法模块的完整构建与测试方案:

  • 使用antlr4-maven-pluginasm8080.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.ASMOS2CCP.ASMOS3BDOS.ASMOS4BIOS.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 test

antlr4test 插件会以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$)、宏展开等工具链功能。表达式规则链(expressionunaryExpressionargument)为在 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),仅供参考

需要专业的网站建设服务?

联系我们获取免费的网站建设咨询和方案报价,让我们帮助您实现业务目标

立即咨询