In a two-pass assembler, the process of converting an assembly language program into machine code occurs in two distinct phases. Pass 1 focuses on gathering essential information, such as creating a symbol table and determining memory locations, while Pass 2 is the actual phase where the assembler generates the executable machine code.
Pass 2 is crucial because it finalizes the translation of assembly language instructions into machine code that can be understood and executed by the CPU. During this phase, the assembler replaces all symbolic names (such as labels and variables) with their actual memory addresses and produces the final object code or executable.
Pass 2 takes the intermediate results produced in Pass 1 and fills in the details required to create the final executable machine code. It is the second and final stage in the assembly process, where the assembler performs the following key tasks:
Translation of Instructions to Machine Code: In Pass 1, the assembler generates a skeleton version of the machine code with placeholders for addresses. In Pass 2, these placeholders are replaced with actual memory addresses, and each instruction is translated into its corresponding machine code.
Symbol Resolution: In Pass 1, labels used in the assembly code were stored in the symbol table with memory addresses to be assigned later. In Pass 2, the assembler looks up these symbols in the table and substitutes them with their actual memory addresses.
Handling Directives: In Pass 1, certain directives (e.g., .data, .text, etc.) were processed. During Pass 2, these directives are translated into machine-readable representations to organize data and code in memory.
Generation of Object Code: The final output of Pass 2 is an object code or machine code file. This file contains the complete set of instructions and data that the CPU can execute.
Final Code Optimization: In some cases, Pass 2 may include code optimization techniques to improve the efficiency or size of the machine code.
Pass 2 is a critical phase that turns the symbolic assembly code into executable machine code, which can be linked to create a complete program.
Algorithm
Pass 2
begin
read first input line (from intermediate file)
if OPCODE = ‘START’ then
begin
write listing line
read next input line
end {if START}
write Header record to object program
initialize first Text record
while OPCODE ≠ ‘END’ do
begin
if this is not a comment line then
begin
search OPTAB for OPCODE
if found then
begin
if there is a symbol in OPERAND field then
begin
search SYMTAB for OPERAND
if found then
store symbol value as operand address
else
begin
store 0 as operand address
set error flag (undefined symbol)
end
end {if symbol}
end {if opcode found}
else
store 0 as operand address
assemble the object code instruction
else if OPCODE = ‘BYTE’ or ‘WORD’ then
convert constant to object code
if object code will not fit into the current Text record then
begin
write Text record to object program
initialize new Text record
end
add object code to Text record
end {if not comment}
write listing line
read next input line
end {while not END}
write last Text record to object program
write End record to object program
write last listing line
end {Pass 2}
Pass 2 Implementation:
Code:#include
#include
#include
#include
void passTwo() {
FILE *intermediate, *optab, *symtab, *objectProgram, *objcodeFile;
char label[10], opcode[10], operand[10], mnemonic[10], line[100];
int locctr, start = 0, operandAddress = 0, baseAddress = 0;
char textRecord[70] = "", objectCode[10], tempObjectCode[10];
int textRecordStart = 0, textRecordLength = 0;
// Open files
intermediate = fopen("pass1_intermediate_code.txt", "r");
optab = fopen("pass1_optab.txt", "r");
symtab = fopen("pass1_symtab.txt", "r");
objectProgram = fopen("objectProgram.txt", "w");
objcodeFile = fopen("objectCode.txt", "w");
if (!intermediate || !optab || !symtab || !objectProgram || !objcodeFile) {
printf("Error: Unable to open one or more files.\n");
exit(1);
}
// Read the first line of the intermediate file
fgets(line, sizeof(line), intermediate);
sscanf(line, "%X %s %s %s", &locctr, label, opcode, operand);
// Check if the first opcode is "START"
if (strcmp(opcode, "START") == 0) {
start = strtol(operand, NULL, 16);
fprintf(objectProgram, "H^%-6s^%06X^%06X\n", label, start, 0); // Header record
textRecordStart = start;
fgets(line, sizeof(line), intermediate);
}
printf("Loc Label Opcode Operand ObjectCode\n");
printf("-------------------------------------------\n");
// Process each line of the intermediate file
while (strcmp(opcode, "END") != 0) {
sscanf(line, "%X %s %s %s", &locctr, label, opcode, operand);
rewind(optab);
int found = 0;
// Search OPTAB for opcode
while (fgets(tempObjectCode, sizeof(tempObjectCode), optab)) {
sscanf(tempObjectCode, "%s %s", mnemonic, tempObjectCode);
if (strcmp(opcode[0] == '+' ? opcode + 1 : opcode, mnemonic) == 0) {
found = 1;
break;
}
}
if (found) {
int format = (opcode[0] == '+') ? 4 : 3; // Determine instruction format
int n = 1, i = 1, x = 0, b = 0, p = 0, e = 0;
// Addressing modes
if (operand[0] == '#') { // Immediate addressing
n = 0;
if (isdigit(operand[1])) {
operandAddress = atoi(operand + 1); // Direct constant
} else {
rewind(symtab);
while (fgets(tempObjectCode, sizeof(tempObjectCode), symtab)) {
char symbol[10];
sscanf(tempObjectCode, "%s %X", symbol, &operandAddress);
if (strcmp(symbol, operand + 1) == 0) break;
}
}
} else if (operand[0] == '@') { // Indirect addressing
i = 0;
rewind(symtab);
while (fgets(tempObjectCode, sizeof(tempObjectCode), symtab)) {
char symbol[10];
sscanf(tempObjectCode, "%s %X", symbol, &operandAddress);
if (strcmp(symbol, operand + 1) == 0) break;
}
} else { // Simple or indexed addressing
char baseOperand[10];
if (strchr(operand, ',') != NULL) { // Indexed addressing
sscanf(operand, "%[^,]", baseOperand);
x = 1;
} else {
strcpy(baseOperand, operand);
}
rewind(symtab);
while (fgets(tempObjectCode, sizeof(tempObjectCode), symtab)) {
char symbol[10];
sscanf(tempObjectCode, "%s %X", symbol, &operandAddress);
if (strcmp(symbol, baseOperand) == 0) break;
}
}
// PC-relative or Base-relative addressing
if (format == 3) {
int disp = operandAddress - (locctr + 3);
if (disp >= -2048 && disp <= 2047) {
p = 1;
operandAddress = disp & 0xFFF; // Mask to 12 bits
} else if (baseAddress != 0 && operandAddress - baseAddress >= 0 && operandAddress - baseAddress <= 4095) {
b = 1;
operandAddress = (operandAddress - baseAddress) & 0xFFF;
}
} else if (format == 4) {
e = 1; // Set extended format bit
}
// Generate object code
int opcodeValue = strtol(tempObjectCode, NULL, 16);
if (format == 4) {
sprintf(objectCode, "%02X%01X%05X", opcodeValue + (n << 1) + i, x * 8 + b * 4 + p * 2 + e, operandAddress);
} else {
sprintf(objectCode, "%02X%01X%03X", opcodeValue + (n << 1) + i, x * 8 + b * 4 + p * 2 + e, operandAddress);
}
// Print object code
fprintf(objcodeFile, "%04X\t%-8s\t%-8s\t%-8s\t%s\n", locctr, label, opcode, operand, objectCode);
printf("%04X %-8s%-8s%-8s%s\n", locctr, label, opcode, operand, objectCode);
// Append object code to text record
if (strlen(textRecord) + strlen(objectCode) > 60) {
fprintf(objectProgram, "T^%06X^%02X^%s\n", textRecordStart, textRecordLength, textRecord);
strcpy(textRecord, "");
textRecordStart = locctr;
textRecordLength = 0;
}
strcat(textRecord, objectCode);
textRecordLength += strlen(objectCode) / 2;
}
fgets(line, sizeof(line), intermediate);
}
// Write the last text record and End record
if (strlen(textRecord) > 0) {
fprintf(objectProgram, "T^%06X^%02X^%s\n", textRecordStart, textRecordLength, textRecord);
}
fprintf(objectProgram, "E^%06X\n", start);
fclose(intermediate);
fclose(optab);
fclose(symtab);
fclose(objectProgram);
fclose(objcodeFile);
printf("Pass 2 completed successfully for SIC/XE.\n");
}
int main() {
passTwo();
return 0;
}
2000 COMPUTE START 2000
2000 SETUP LDX #3
2003 - LDA #2
2006 PROCESS MUL DELTA,X
2009 - TIX INDEX
200C - JLT PROCESS
200F - +STA #FINAL
2013 INDEX RESW 1
2016 FINAL RESW 1
2019 DELTA RESB 60
2055 - END SETUP
LDA 00
LDX 04
MUL 20
TIX 2C
JLT 38
STA 0C
SETUP 2000
PROCESS 2006
INDEX 2013
FINAL 2016
DELTA 2019
2000 SETUP LDX #3 010003
2003 - LDA #2 000002
2006 PROCESS MUL DELTA,X 200300
2009 - TIX INDEX 2C1006
200C - JLT PROCESS 381FF9
200F - +STA #FINAL 0C0002016
H^COMPUTE^002000^000055
T^002000^1E^0100030000022003002C1006381FF90C0002016
E^002000