A macro processor is a system program that expands macros in a source file before the code is assembled or compiled. A macro is a named block of assembly statements; every time its name appears in the program the processor substitutes the full block in place of the call. This eliminates repetition and makes large assembly programs easier to maintain.
This C implementation reads an assembly program from MACIN.TXT, detects MACRO / MEND delimiters to populate a Macro Definition Table (MDT), and then expands every macro call by inline-substituting the stored body. The expanded output is written to MACOUT.TXT and the raw MDT is saved to MDT.TXT.
C Code
#include <stdio.h>
#include <conio.h>
#include <string.h>
/* MDT entry: one line inside a macro body */
struct mdt {
char lab[10]; /* label field */
char opc[10]; /* opcode field */
char oper[10]; /* operand field */
} d[10]; /* up to 10 lines per macro body */
void main() {
char label[10], opcode[10], operand[10];
char macroname[10]; /* name of the most recently defined macro */
int i, lines; /* lines = number of statements in macro body */
FILE *f1, *f2, *f3;
clrscr();
f1 = fopen("MACIN.txt", "r"); /* input assembly program */
f2 = fopen("MACOUT.txt", "w"); /* expanded output program */
f3 = fopen("MDT.txt", "w"); /* macro definition table (debug) */
/* Read the first token triple from the input file */
fscanf(f1, "%s %s %s", label, opcode, operand);
/* Process until END directive is reached */
while (strcmp(opcode, "END") != 0) {
if (strcmp(opcode, "MACRO") == 0) {
/* -----------------------------------------------
Macro definition found:
label holds the macro name; body follows until MEND
----------------------------------------------- */
strcpy(macroname, label);
fscanf(f1, "%s%s%s", label, opcode, operand);
lines = 0;
while (strcmp(opcode, "MEND") != 0) {
/* Store each body line in MDT array and MDT file */
fprintf(f3, "%s\t%s\t%s\n", label, opcode, operand);
strcpy(d[lines].lab, label);
strcpy(d[lines].opc, opcode);
strcpy(d[lines].oper, operand);
fscanf(f1, "%s %s %s", label, opcode, operand);
lines++; /* count statements in the macro body */
}
} else if (strcmp(opcode, macroname) == 0) {
/* -----------------------------------------------
Macro call found:
expand by writing all stored body lines to output
----------------------------------------------- */
printf("lines=%d\n", lines);
for (i = 0; i < lines; i++) {
fprintf(f2, "%s\t%s\t%s\n", d[i].lab, d[i].opc, d[i].oper);
printf("DLAB=%s\nDOPC=%s\nDOPER=%s\n",
d[i].lab, d[i].opc, d[i].oper);
}
} else {
/* Not a macro definition or call: copy line as-is */
fprintf(f2, "%s\t%s\t%s\n", label, opcode, operand);
}
/* Advance to the next line */
fscanf(f1, "%s%s%s", label, opcode, operand);
}
/* Write the final END line to the output */
fprintf(f2, "%s\t%s\t%s\n", label, opcode, operand);
fclose(f1);
fclose(f2);
fclose(f3);
printf("FINISHED");
getch();
}
Input File (MACIN.TXT)
CALC START 1000
SUM MACRO **
** LDA #5
** ADD #10
** STA 2000
** MEND **
** LDA LENGTH
** COMP ZERO
** JEQ LOOP
** SUM **
LENGTH WORD S
ZERO WORD S
LOOP SUM **
** END **
How the Code Works
- File setup: Three files are opened —
MACIN.TXTfor reading the source,MACOUT.TXTfor writing the expanded output, andMDT.TXTfor logging the macro definition table. - Macro definition detection: When the parser sees
MACROin the opcode field, the label on that line is saved as the macro name. Every subsequent line up toMENDis stored in thed[]MDT array and also written toMDT.TXT. The variablelinescounts how many body statements were stored. - Macro call expansion: When the opcode matches
macroname, the processor iterates over alllinesentries ind[]and writes each one toMACOUT.TXT, effectively inlining the macro body at every call site. - Pass-through lines: Any line that is neither a macro definition nor a macro call is copied unchanged to
MACOUT.TXT. - Termination: The loop exits when
ENDis encountered; theENDline itself is then appended to the output.
Output Files
Macro Definition Table (MDT.TXT)
** LDA #5
** ADD #10
** STA 2000
Expanded Output (MACOUT.TXT)
CALC START 1000
** LDA LENGTH
** COMP ZERO
** JEQ LOOP
** LDA #5
** ADD #10
** STA 2000
LENGTH WORD S
ZERO WORD S
** LDA #5
** ADD #10
** STA 2000
** END **
Output Explanation
CALC START 1000is not a macro definition or call, so it is copied verbatim to the output.- The
SUM MACROblock is consumed during definition and does not appear in the output. Its three body lines (LDA #5,ADD #10,STA 2000) are stored in the MDT. - Lines
LDA LENGTH,COMP ZERO,JEQ LOOPare regular instructions; they are passed through unchanged. - First
SUMcall (line 10): The macro is expanded inline — the three MDT lines are inserted, replacing the singleSUM **call. LENGTH WORD SandZERO WORD Sare data declarations; they are copied unchanged.- Second
SUMcall (LOOP SUM **): The same three MDT lines are expanded again, demonstrating that a macro can be called any number of times. ENDis appended as the final line of the expanded program.
See Also
- Implementing Multi-pass Assembler in C
- Implementing Absolute Loader in C++
- Implementing Code Generator in C++
- Implementing Lexical Analyser in C++
- Implementation of Bottom-Up (Shift-Reduce) Parsing in C++
Conclusion
This single-pass macro processor captures the essential idea behind all macro systems: define once, expand everywhere. The MDT array plays the same role as the Macro Definition Table in a full assembler, and the expansion loop mirrors what production assemblers do when they encounter macro calls. Pairing this processor with the multi-pass assembler gives you a complete front-to-back pipeline: the macro processor runs first to eliminate all macro calls, and the assembler then translates the expanded source into object code.
This code is not workable for me it produces error segmention fault .Iam running it in Cent Os.
reply plss
i know the reason behind the error ‘segmentation fault (core dumped)… if you want it then mail me… my id is
[email protected]
Thank you bro <3