Revision 3ab1daba66822703a7bb801e153d40acfed9f7a3 authored by Ron Burkey on 27 October 2022, 03:41:42 UTC, committed by Ron Burkey on 27 October 2022, 03:41:42 UTC
1 parent e5337f6
yaASM.c
/*
* Copyright 2011,2012,2019,2020,2022 Ronald S. Burkey <info@sandroid.org>
*
* This file is part of yaAGC.
*
* yaAGC is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation; either version 2 of the License, or
* (at your option) any later version.
* yaAGC is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with yaAGC; if not, write to the Free Software
* Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
*
* Filename: yaASM.c
* Purpose: Cross-assembler for Gemini OBC. Was at one time intended
* also for the Apollo LVDC, but that is no longer the case.
* (For LVDC, refer to yaASM.py instead.)
* Compiler: GNU gcc.
* Reference: http://www.ibibio.org/apollo
* Mods: 2010-01-30 RSB Began adapting from yaLEMAP.c.
* 2011-12-08 RSB I've completely discarded the previous
* uncompleted work, and have started
* from scratch based on input from
* original OBC developers
* and on the fact that I had clearly
* bit off more than I could chew by
* trying to shoe-horn this into the
* framework of yaLEMAP ... yes, I
* lose some capabilities by doing so,
* but better to have an assembler with
* reduced capabilities than never to
* have one at all if I can't bring it
* to fruition!
* 2011-12-16 RSB I think it's fully functional right
* now, with at least the minimal set of
* new features that I think needed to be
* added to the original ... though not
* necessarily completely debugged.
* 2011-12-20 RSB Corrected the SPQ instruction, which
* for some reason I thought stored the
* result in the accumulator rather than
* in memory.
* 2011-12-23 RSB Implemented program modules for OBC ...
* turns out to be implemented exactly
* the same way as memory modules for LVDC!
* Also changed the binary output slightly
* in order to incorporate a starting
* HOP constant.
* 2011-12-27 RSB Was using wrong sector as residual.
* 2012-01-07 RSB Added HOP extensions for HOP, CLA,
* and STO instructions.
* 2012-01-08 RSB Fixed to not abort instantly on
* error detection.
* 2019-09-08 RSB Backed out anything related to LVDC
* support. Use yaASM.py instead for LVDC.
* 2020-12-06 RSB Changed character stuffed at end of input
* line buffer to avoid a clang warning for
* signed vs unsigned characters.
* 2022-08-07 RSB Removed the assembler's timestamp from
* the assembly listing, in favor of a
* yaASM revision code (1.0). Also,
* the latest compiler version now detects
* a potential string overflow, which I've
* worked around.
*/
#include <stdlib.h>
#include <stdio.h>
#include <string.h>
#include <ctype.h>
#include <math.h>
#include <stdint.h>
#include <time.h>
#include "./yaASM.h"
#define REVISION "1.0"
// Command-line options.
static int HalfWordMode = 0; // Can change during assembly.
static Address_t InstructionPointer, StartingInstructionPointer =
{ 0, 0, 2, 0 }, ObcEntry =
{ 0, 0, 2, 0 };
static Address_t DataPointer, StartingDataPointer =
{ 0, 0, 0, 0 };
enum PassType_t
{
PT_SYMBOLS, PT_CODE
};
static int
Pass(enum PassType_t PassType);
// Line- and field-buffers.
#define MAX_FIELDS 4
static Line_t InputLine, Comment, Fields[MAX_FIELDS];
static char *FieldStarts[MAX_FIELDS];
static int NumFields;
// Symbol table.
static SymbolList_t Symbols;
static int NumSymbols = 0;
// Variables for tracking files and lines.
static int ErrorCount = 0;
static char *Input = NULL;
static FILE *fin = NULL;
static int LineTotal = 0, LineInFile = 0;
typedef struct
{
int LineInFile;
FILE *fin;
Line_t Input;
} File_t;
#define MAXFILEDEPTH 16
static File_t Files[MAXFILEDEPTH];
static int CurrentFileDepth = 0;
// Buffer for the binary. This is initially filled
// with the illegal value 0xFFFF, and we use that later
// to detect uninitialized memory or else memory that
// has been overwritten (at assembly time).
static BinaryImage_t Binary;
// List of opcodes and pseudo-ops.
static const char *Operators[] =
{ "HOP", "DIV", "PRO", "RSU", "ADD", "SUB", "CLA", "AND", "MPY", "TRA",
"SHF", "TMI", "STO", "SPQ", "CLD", "TNZ", "NOP", "SHR", "SHL", "DEC",
"OCT", "SYN", "EQU", "HOPC" };
enum OperandType_t
{
OT_ADDRESS, OT_YX, OT_CYX, OT_NONE, OT_SHR, OT_SHL, OT_NOP
};
enum AddressType_t
{
AT_DATA, AT_CODE
};
enum Operator_t
{
OP_START = 0,
OP_HOP = OP_START,
OP_DIV,
OP_PRO,
OP_RSU,
OP_ADD,
OP_SUB,
OP_CLA,
OP_AND,
OP_MPY,
OP_TRA,
OP_SHF,
OP_TMI,
OP_STO,
OP_SPQ,
OP_CLD,
OP_TNZ,
OP_OFFICIAL,
OP_NOP = OP_OFFICIAL,
OP_SHR,
OP_SHL,
OP_OPCODES,
OP_DEC = OP_OPCODES,
OP_OCT,
OP_ALLOCS,
OP_SYN = OP_ALLOCS,
OP_EQU,
OP_HOPC,
OP_COUNT
};
typedef struct
{
int NumericalOpCode;
enum OperandType_t OperandType;
enum AddressType_t AddressType; // Valid only for OperandType==OT_ADDRESS.
} ParseType_t;
static const ParseType_t ParseTypes[OP_OPCODES] =
{
{ 00, OT_ADDRESS }, // HOP
{ 01, OT_ADDRESS, AT_DATA }, // DIV
{ 02, OT_CYX }, // PRO
{ 03, OT_ADDRESS, AT_DATA }, // RSU
{ 04, OT_ADDRESS, AT_DATA }, // ADD
{ 05, OT_ADDRESS, AT_DATA }, // SUB
{ 06, OT_ADDRESS, AT_DATA }, // CLA
{ 07, OT_ADDRESS, AT_DATA }, // AND
{ 010, OT_ADDRESS, AT_DATA }, // MPY
{ 011, OT_ADDRESS, AT_CODE }, // TRA
{ 012, OT_YX }, // SHF
{ 013, OT_ADDRESS, AT_CODE }, // TMI
{ 014, OT_ADDRESS, AT_DATA }, //STO
{ 015, OT_ADDRESS, AT_DATA }, // SPQ
{ 016, OT_YX }, // CLD
{ 017, OT_ADDRESS, AT_CODE }, // TNZ
{ 011, OT_NOP }, // NOP
{ 012, OT_SHR }, // SHR
{ 012, OT_SHL } };
/////////////////////////////////////////////////////////////////////////
// Various helper functions.
// Write the value of a symbol to binary.
static int
WriteBinary(enum SymbolType_t Type, Address_t *Address, uint32_t Value)
{
int i, Count = 0, RetVal = 0;
uint16_t *Destination;
if (Type == ST_CODE)
Count = 1;
else if (Type == ST_VARIABLE || Type == ST_CONSTANT)
Count = (Address->HalfWordMode ? 1 : 2); // Detect half-word mode.
for (i = 0; i < Count; i++)
{
Destination = &Binary[Address->Module][Address->Page][Address->Syllable
+ i][Address->Word];
if (*Destination != ILLEGAL_VALUE)
{
RetVal++;
fprintf(stderr, "Trying to overwrite binary at address ");
fprintf(stderr, "%o-", Address->Module);
fprintf(stderr, "%02o-%o-%03o\n", Address->Page, Address->Syllable,
Address->Word);
}
else
*Destination = 0x1FFF & (Value >> (13 * i));
}
return (RetVal);
}
// Compares two Symbol_t structures on the basis of symbol
// name, for use with qsort() or bsearch().
static int
CompareSymbols(const void *s1, const void *s2)
{
#define Sym1 ((Symbol_t *) s1)
#define Sym2 ((Symbol_t *) s2)
return (strcmp(Sym1->Name, Sym2->Name));
}
// Compares two Symbol_t structures based on memory address,
// for use with qsort() or bsearch().
static int
CompareAddresses(const void *s1, const void *s2)
{
#define Sym1 ((Symbol_t *) s1)
#define Sym2 ((Symbol_t *) s2)
int i;
i = Sym1->Address.Module - Sym2->Address.Module;
if (i)
return (i);
i = Sym1->Address.Page - Sym2->Address.Page;
if (i)
return (i);
i = Sym1->Address.Syllable - Sym2->Address.Syllable;
if (i)
return (i);
i = Sym1->Address.Word - Sym2->Address.Word;
if (i)
return (i);
i = Sym1->RefType - Sym2->RefType;
if (i)
return (i);
return (strcmp(Sym1->Name, Sym2->Name));
}
// This function finds all of the fields in the input line
// (up to a maximum of MAX_FIELDS), and stores them in
// the Fields[] array. It also stores (in the FieldStarts[])
// array pointers to the starts of the fields within the
// input line. The practical difference between Fields[]
// and FieldStarts[] is that the fields are nul-terminated
// in the former and continue to the end of the line in the
// latter.
static void
ParseToFields(void)
{
char *s, *ss, c;
int i;
for (i = 0; i < MAX_FIELDS; i++)
{
Fields[i][0] = 0;
FieldStarts[i] = NULL;
}
for (i = 0, s = InputLine; i < MAX_FIELDS; i++, s = ss)
{
for (; isspace (*s); s++)
; // Move past white-space.
if (!*s) // End of the line?
break;
FieldStarts[i] = s;
for (ss = s; *ss && !isspace (*ss); ss++)
; // Move to end of field.
c = *ss;
*ss = 0;
strcpy(Fields[i], s);
*ss = c;
}
NumFields = i;
}
// Add a SYN/EQU/HOPC back-reference to the symbol listing.
static void
PrintRef(Symbol_t *Symbol)
{
if (Symbol->RefType == ST_NONE)
;
else if (Symbol->RefType == ST_HOPLHS)
printf(", created indirectly via LHS operand in HOP/CLS/STO");
else
{
printf(", created via \"");
if (Symbol->RefType == ST_SYN)
printf("SYN");
else if (Symbol->RefType == ST_EQU)
printf("EQU");
else if (Symbol->RefType == ST_HOPC)
printf("HOPC");
printf(" %s\"", Symbol->RefName);
}
}
/////////////////////////////////////////////////////////////////////////
// The main program.
int
main(int argc, char *argv[])
{
int i, j, k, n, RetVal = 1;
int m, p, s, w;
uint16_t *u;
FILE *OutFile;
// Initialize the binary as completely unused.
for (i = 0, u = &Binary[0][0][0][0]; i < MAX_ADDRESSES; i++, u++)
*u = ILLEGAL_VALUE;
// Parse the command-line options.
for (i = 1; i < argc; i++)
{
if (!strcmp(argv[i], "--help"))
{
RetVal = 0;
Help: ;
fprintf(stderr, "USAGE:\n");
fprintf(stderr, "\tyaASM [OPTIONS] --input=Input.obc >Output.lst\n");
fprintf(stderr, "The binary (if any) is output to yaASM.bin.\n");
fprintf(stderr, "The available OPTIONS are:\n");
fprintf(stderr, "--help Shows this help-menu.\n");
fprintf(stderr, "--hwm Start assembly in \"half-word\n");
fprintf(stderr, " mode\". (HALF or NORM directives\n");
fprintf(stderr, " within the source code itself can\n");
fprintf(stderr, " change the mode.)\n");
fprintf(stderr,
"--code=M-P-S-W Starting address for instructions.\n");
fprintf(stderr,
" M is the module number (0-7). P is\n");
fprintf(stderr,
" the page number in octal (0-17). S is\n");
fprintf(stderr,
" the syllable number (0,1,2). W is the\n");
fprintf(stderr,
" word number in octal (0-377). CODE\n");
fprintf(stderr,
" directives embedded within the source\n");
fprintf(stderr,
" code can change this.\n");
fprintf(stderr,
"--data=M-P-S-W Starting address for data. The same\n");
fprintf(stderr,
" interpretations apply as for --code, except\n");
fprintf(stderr,
" that for the OBC S=2 is legal only with\n");
fprintf(stderr,
" --hwm and S=0 is legal only without --hwm,\n");
fprintf(stderr,
" while S=1 is never legal. DATA directives\n");
fprintf(stderr,
" within the source code can change the data\n");
fprintf(stderr, " pointer as well.\n");
goto Done;
}
else if (!strncmp(argv[i], "--input=", 8))
Input = &argv[i][8];
else if (!strcmp(argv[i], "--hwm"))
HalfWordMode = 1;
else if (4 == sscanf(argv[i], "--code=%d-%d-%d-%d", &m, &p, &s, &w))
{
StartingInstructionPointer.Module = m;
StartingInstructionPointer.Page = p;
StartingInstructionPointer.Syllable = s;
StartingInstructionPointer.Word = w;
goto ParseAddressForError;
}
else if (4 == sscanf(argv[i], "--data=%d-%d-%d-%d", &m, &p, &s, &w))
{
StartingDataPointer.Module = m;
StartingDataPointer.Page = p;
StartingDataPointer.Syllable = s;
StartingDataPointer.Word = w;
ParseAddressForError: ;
//if (m != 0)
// {
// fprintf(stderr, "For OBC, module number must be 0.\n");
// goto Help;
// }
if (m < 0 || m >= MAX_MODULES)
{
fprintf(stderr, "Module number out of range.\n");
goto Help;
}
if (p < 0 || p >= MAX_SECTORS)
{
fprintf(stderr, "Sector number out of range.\n");
goto Help;
}
if (s < 0 || s >= MAX_SYLLABLES)
{
fprintf(stderr, "Syllable number out of range.\n");
goto Help;
}
if (w < 0 || w >= MAX_WORDS)
{
fprintf(stderr, "Word-number out of range.\n");
goto Help;
}
}
else
{
fprintf(stderr, "Unrecognized option: %s\n", argv[i]);
goto Help;
}
}
if (HalfWordMode && DataPointer.Syllable != 2)
{
fprintf(stderr, "Syllable must be 2 for data in half-word mode.\n");
goto Help;
}
if (!HalfWordMode && DataPointer.Syllable != 0)
{
fprintf(stderr,
"Syllable must be 0 for data when OBC is not in half-word mode.\n");
goto Help;
}
if (Input == NULL)
{
fprintf(stderr, "No input file specified.\n");
goto Help;
}
fin = fopen(Input, "rb");
if (fin == NULL)
{
fprintf(stderr, "Could not open input file %s\n", Input);
goto Help;
}
// First pass on the input. This pass simply determines the
// memory locations for all left-hand symbols, variables, and
// constants.
if (Pass(PT_SYMBOLS))
goto Done;
// Let's check the symbol table for duplicates and sort for
// faster access later.
qsort(Symbols, NumSymbols, sizeof(Symbol_t), CompareSymbols);
for (i = 1, j = 0; i < NumSymbols; i++)
if (!strcmp(Symbols[i - 1].Name, Symbols[i].Name))
{
j++;
printf(
"The symbol %s had multiple definitions, at line %d and line %d.\r\n",
Symbols[i].Name, Symbols[i - 1].Line, Symbols[i].Line);
ErrorCount++;
}
if (j)
goto Done;
// Resolve all SYNs, EQUs, and HOPCs in the symbol table before proceeding.
// Because of the way SYN works, we may not be able to resolve all symbols
// on the first pass, so we keep performing passes until the final pass
// didn't resolve any new symbols. In this process, n is the number of symbols
// resolved on the pass, j is the number of symbols left unresolved in the
// pass, k counts the passes, and i is for looping through the symbols during
// the pass.
for (k = j = n = 1; n != 0 && j != 0; k++)
{
n = j = 0;
for (i = 0; i < NumSymbols; i++)
{
enum SymbolType_t Type;
Symbol_t *RefSymbol, Key;
Type = Symbols[i].Type;
if (Type != ST_SYN && Type != ST_EQU && Type != ST_HOPC)
continue;
strcpy(Key.Name, Symbols[i].RefName);
RefSymbol = bsearch(&Key, Symbols, NumSymbols, sizeof(Symbol_t),
CompareSymbols);
if (RefSymbol == NULL || RefSymbol->Type == ST_SYN || RefSymbol->Type
== ST_EQU || RefSymbol->Type == ST_HOPC)
{
j++;
fprintf(
stderr,
"FYI: On pass %d, symbol %s referenced by symbol %s not resolved.\n",
k, Symbols[i].RefName, Symbols[i].Name);
continue;
}
n = 1;
switch (Type)
{
case ST_SYN:
memcpy(&Symbols[i].Address, &RefSymbol->Address, sizeof(Address_t));
Symbols[i].RefType = ST_SYN;
Symbols[i].Type = RefSymbol->Type;
Symbols[i].Value = RefSymbol->Value;
break;
case ST_EQU:
if (RefSymbol->Type != ST_CONSTANT)
{
fprintf(stderr,
"EQU for %s->%s inappropriate since %s not a constant.\n",
Symbols[i].Name, Key.Name, Key.Name);
goto Done;
}
Symbols[i].Type = ST_CONSTANT;
Symbols[i].RefType = ST_EQU;
Symbols[i].Value = RefSymbol->Value;
if (WriteBinary(ST_CONSTANT, &Symbols[i].Address, Symbols[i].Value))
{
fprintf(stderr, "Aborting resolution of \"%s EQU %s\".\n",
Symbols[i].Name, Symbols[i].RefName);
goto Done;
}
break;
case ST_HOPC:
if (RefSymbol->Type != ST_CODE)
{
fprintf(
stderr,
"EQU for %s->%s inappropriate since %s does not point to code.\n",
Symbols[i].Name, Key.Name, Key.Name);
goto Done;
}
else
{
int OperandValue;
Symbols[i].Type = ST_CONSTANT;
Symbols[i].RefType = ST_HOPC;
OperandValue = RefSymbol->Address.Word & 0377;
if (RefSymbol->Address.HalfWordMode)
OperandValue |= 0400000;
OperandValue |= (RefSymbol->Address.Syllable & 3) << 14;
OperandValue |= (RefSymbol->Address.Page & 0x0F) << 9;
if (RefSymbol->Address.Page == RESIDUAL_SECTOR)
OperandValue |= 0400;
Symbols[i].Value = OperandValue;
if (WriteBinary(ST_CONSTANT, &Symbols[i].Address,
Symbols[i].Value))
{
fprintf(stderr, "Aborting resolution of \"%s HOPC %s\".\n",
Symbols[i].Name, Symbols[i].RefName);
goto Done;
}
}
break;
;
default: // Can't actually get here, due to a test made earlier.
break;
}
}
}
fprintf(
stderr,
"FYI: %d symbol-resolution passes performed, %d symbols remain unresolved.\n",
k - 1, j);
if (j)
goto Done;
// Final pass on the input. This pass generates the output
// binary and listing.
rewind(fin);
if (Pass(PT_CODE))
goto Done;
// Output the symbol table.
printf(NL);
printf("ALPHABETIZED SYMBOL TABLE" NL);
printf("-------------------------" NL);
for (i = 0; i < NumSymbols; i++)
{
printf("%-8s at address ", Symbols[i].Name);
printf("%o-", Symbols[i].Address.Module);
printf("%02o-%o-%03o: ", Symbols[i].Address.Page,
Symbols[i].Address.Syllable, Symbols[i].Address.Word);
switch (Symbols[i].Type)
{
case ST_CODE:
printf("Left-hand symbol");
PrintRef(&Symbols[i]);
if (!strcmp(Symbols[i].Name, "OBCENTRY"))
memcpy(&ObcEntry, &Symbols[i].Address, sizeof(Address_t));
break;
case ST_VARIABLE:
printf("Uninitialized variable");
PrintRef(&Symbols[i]);
break;
case ST_CONSTANT:
printf("Constant (%09o)", Symbols[i].Value);
PrintRef(&Symbols[i]);
break;
case ST_SYN:
case ST_EQU:
case ST_HOPC:
printf("Unresolved SYM, EQU, or HOPC (implementation error): %s",
Symbols[i].RefName);
break;
default:
printf("Unknown type (implementation error)");
break;
}
printf(NL);
}
// Resort the symbol table by address and print it.
qsort(Symbols, NumSymbols, sizeof(Symbol_t), CompareAddresses);
printf(NL);
printf("SYMBOL TABLE, BY ADDRESS" NL);
printf("------------------------" NL);
for (i = 0; i < NumSymbols; i++)
{
printf("%o-", Symbols[i].Address.Module);
printf("%02o-%o-%03o, ", Symbols[i].Address.Page,
Symbols[i].Address.Syllable, Symbols[i].Address.Word);
printf("%-8s: ", Symbols[i].Name);
switch (Symbols[i].Type)
{
case ST_CODE:
printf("Left-hand symbol");
PrintRef(&Symbols[i]);
break;
case ST_VARIABLE:
printf("Uninitialized variable");
PrintRef(&Symbols[i]);
break;
case ST_CONSTANT:
printf("Constant (%09o)", Symbols[i].Value);
PrintRef(&Symbols[i]);
break;
case ST_SYN:
case ST_EQU:
case ST_HOPC:
printf("Unresolved SYM, EQU, or HOPC (implementation error): %s",
Symbols[i].RefName);
break;
default:
printf("Unknown type (implementation error)");
break;
}
printf(NL);
}
// Binary listing.
printf(NL);
printf("OCTAL LISTING (SYL2-SYL1-SYL0)" NL);
printf("------------------------------" NL);
// In this loop, j is a state variable that we use to
// avoid printing chunks of uninitialized memory.
j = 0;
for (m = 0; m < MAX_MODULES; m++)
for (p = 0; p < MAX_SECTORS; p++)
for (w = 0; w < MAX_WORDS; w++)
{
// We'll print 4 words per line.
if (0 == (w & 3))
{
// Is the entire block uninitialized?
for (s = 0; s < MAX_SYLLABLES; s++)
for (k = 0; k < 4; k++)
if (Binary[m][p][s][w + k] != ILLEGAL_VALUE)
{
s = 100;
k = 100;
}
if (s >= 100)
j = 0;
else
{
if (j)
{
w += 3;
continue; // No need to print anything for this block.
}
j = 1;
}
printf("%o-", m);
printf("%02o-N-%03o:", p, w);
if (j)
{
printf(
" ----------------------------- (uninitialized) ----------------------------" NL);
w += 3;
continue;
}
}
printf(" ");
if (Binary[m][p][2][w] == ILLEGAL_VALUE)
printf("XXXXX");
else
printf("%05o", Binary[m][p][2][w]);
printf("-");
if (Binary[m][p][1][w] == ILLEGAL_VALUE)
printf("XXXXX");
else
printf("%05o", Binary[m][p][1][w]);
printf("-");
if (Binary[m][p][0][w] == ILLEGAL_VALUE)
printf("XXXXX");
else
printf("%05o", Binary[m][p][0][w]);
if (3 == (w & 3))
printf(NL);
}
// Output the binary for use by the emulator if/when it's created.
OutFile = fopen("yaASM.bin", "wb");
if (OutFile == NULL)
{
fprintf(stderr, "Error: Cannot create the output file yaASM.bin.\n");
goto Done;
}
else
{
uint32_t HopRegister = 0, Accumulator = 0, PqRegister = 0;
HopRegister = ObcEntry.Word & 0777;
HopRegister |= (ObcEntry.Page & 017) << 9;
HopRegister |= (ObcEntry.Syllable & 03) << 14;
HopRegister |= (ObcEntry.HalfWordMode & 01) << 17;
if (1 != fwrite(Binary, sizeof(Binary), 1, OutFile) || 1 != fwrite(
&HopRegister, sizeof(HopRegister), 1, OutFile) || 1 != fwrite(
&Accumulator, sizeof(Accumulator), 1, OutFile) || 1 != fwrite(
&PqRegister, sizeof(PqRegister), 1, OutFile))
{
fclose(OutFile);
fprintf(stderr, "Error: Write-error on output file yaASM.bin.\n");
goto Done;
}
fclose(OutFile);
}
if (ErrorCount == 0)
RetVal = 0;
Done: ;
if (fin != NULL)
fclose(fin);
printf("\r\n%d error(s) detected.\r\n", ErrorCount);
return (RetVal);
}
/////////////////////////////////////////////////////////////////////////
// Perform a single pass on the source code.
static int
Pass(enum PassType_t PassType)
{
int i, RetVal = 1, OperandIsInteger, OperandValue;
double fOperandValue;
enum Operator_t Operator;
//char c, *s, *ss;
char *sss;
Address_t *Address; // A dummy.
memcpy(&InstructionPointer, &StartingInstructionPointer, sizeof(Address_t));
memcpy(&DataPointer, &StartingDataPointer, sizeof(Address_t));
LineTotal = LineInFile = 0;
if (PassType == PT_CODE)
{
time_t t;
time(&t);
printf(
"Listing created by OBC assembler yaASM rev " REVISION NL);
printf("Source file %s processed %s" NL, Input, (char *) ctime(&t));
}
while (1)
{
int Commented = 0, LeftHandSymboled = 0, Operatored = 0, CurrentField = 0;
enum SymbolType_t SymbolType = ST_NONE;
char FirstChar;
char OperandBuffer[MAX_SYMSIZE + 1], *OperandField;
InputLine[LINESIZE - 1] = 127; // 20201206 RSB, was 255.
if (NULL == fgets(InputLine, sizeof(Line_t), fin)) // Done with this file?
{
if (CurrentFileDepth == 0) // Done with ALL files?
break;
fclose(fin);
// Pop parent file and proceed with processing it.
CurrentFileDepth--;
fin = Files[CurrentFileDepth].fin;
Input = Files[CurrentFileDepth].Input;
LineInFile = Files[CurrentFileDepth].LineInFile;
continue;
}
LineTotal++;
LineInFile++;
if (InputLine[LINESIZE - 1] == 0)
{
//fprintf(stderr, "%s:%d: error: Line too long.\n", Input, LineInFile);
//goto Done;
printf("%s:%d: error: Line too long.\r\n", Input, LineInFile);
ErrorCount++;
}
FirstChar = InputLine[0];
sss = strstr(InputLine, "\n"); // Trim off trailing '\n'.
if (sss != NULL)
*sss = 0;
sss = strstr(InputLine, "\r"); // Trim off trailing '\r'.
if (sss != NULL)
*sss = 0;
sss = strstr(InputLine, "#"); // Pick off obvious comments;
if (sss != NULL)
{
strcpy(Comment, sss + 1);
*sss = 0;
Commented = 1;
}
// Parse all of the fields in the input line.
ParseToFields();
// Pick off the first field in the line.
if (NumFields == 0) // Blank line?
{
if (PassType == PT_CODE)
{
if (Commented)
{
printf(" ");
if (FirstChar == '#')
printf(" \t#%s", Comment);
else
printf(
" \t \t#%s",
Comment);
}
printf(NL);
}
continue;
}
CurrentField = 0;
// Is this a file-include directive?
if (Fields[CurrentField][0] == '$')
{
if (strlen(Fields[CurrentField]) == 1)
{
//fprintf(stderr, "%s:%d: error: No include-file specified.\n",
// Input, LineInFile);
//goto Done;
printf("%s:%d: error: No include-file specified.\r\n", Input,
LineInFile);
ErrorCount++;
}
// Push current file and open the include-file instead.
if (CurrentFileDepth >= MAXFILEDEPTH)
{
printf("%s:%d: error: Too many nested include-files.\r\n", Input,
LineInFile);
ErrorCount++;
fprintf(stderr, "%s:%d: error: Too many nested include-files.\n",
Input, LineInFile);
goto Done;
}
Files[CurrentFileDepth].LineInFile = LineInFile;
Files[CurrentFileDepth].fin = fin;
strcpy(Files[CurrentFileDepth].Input, Input);
CurrentFileDepth++;
Input = &Fields[CurrentField][1];
// Check what's left of the line.
CurrentField++;
if (CurrentField < NumFields)
{
if (Commented)
fprintf(
stderr,
"%s:%d: warning: Garbage between include-directive and comment: %s\n",
Input, LineInFile, FieldStarts[CurrentField]);
else
{
strcpy(Comment, FieldStarts[CurrentField]);
Commented = 1;
}
}
if (PassType == PT_CODE)
{
// ... output to listing ...
}
fin = fopen(Input, "rb");
LineInFile = 0;
if (fin == NULL)
{
printf(
"%s:%d: error: Include-file %s not found or too many files open.\r\n",
Input, LineInFile, Input);
ErrorCount++;
fprintf(
stderr,
"%s:%d: error: Include-file %s not found or too many files open.\n",
Input, LineInFile, Input);
goto Done;
}
continue;
}
// Is this a pointer change?
if (!strcmp(Fields[0], "HALF"))
{
HalfWordMode = 1;
InstructionPointer.HalfWordMode = HalfWordMode;
if (PassType == PT_CODE)
{
printf(" ");
printf(" \t %s" NL, Fields[0]);
}
continue;
}
if (!strcmp(Fields[0], "NORM"))
{
HalfWordMode = 0;
InstructionPointer.HalfWordMode = HalfWordMode;
if (PassType == PT_CODE)
{
printf(" ");
printf(" \t %s" NL, Fields[0]);
}
continue;
}
if (!strcmp(Fields[0], "DATA") || !strcmp(Fields[0], "CODE"))
{
int m, p, s, w;
if (NumFields < 2)
{
//fprintf(stderr,
// "%s:%d: error: DATA or CODE require an operand.\n", Input,
// LineInFile);
//goto Done;
printf("%s:%d: error: DATA or CODE require an operand.\r\n",
Input, LineInFile);
ErrorCount++;
strcpy(Fields[1], "0-00-0-000");
NumFields++;
}
if (NumFields > 2)
{
//fprintf(stderr,
// "%s:%d: error: Garbage following operand for DATA/CODE.\n",
// Input, LineInFile);
//goto Done;
printf(
"%s:%d: error: Garbage following operand for DATA/CODE.\r\n",
Input, LineInFile);
ErrorCount++;
NumFields = 2;
}
if (4 != sscanf(Fields[1], "%o-%o-%o-%o", &m, &p, &s, &w))
{
//fprintf(stderr,
// "%s:%d: error: Operand for DATA/CODE needs 4 fields.\n",
// Input, LineInFile);
//goto Done;
printf("%s:%d: error: Operand for DATA/CODE needs 4 fields.\r\n",
Input, LineInFile);
ErrorCount++;
m = p = s = w = 0;
}
if (m < 0 || m >= MAX_MODULES || p < 0 || p >= MAX_SECTORS || s < 0
|| s > 3 || w < 0 || w > MAX_WORDS)
{
//fprintf(stderr,
// "%s:%d: error: Operand for DATA/CODE out of range.\n",
// Input, LineInFile);
//goto Done;
printf("%s:%d: error: Operand for DATA/CODE out of range.\r\n",
Input, LineInFile);
ErrorCount++;
m = p = s = w = 0;
}
if (!strcmp(Fields[0], "CODE"))
{
InstructionPointer.Module = m;
InstructionPointer.Page = p;
InstructionPointer.Syllable = s;
InstructionPointer.Word = w;
InstructionPointer.HalfWordMode = HalfWordMode;
}
else
{
if (s == 1)
{
//fprintf(stderr,
// "%s:%d: error: Data syllable for OBC must be 0 or 2.\n",
// Input, LineInFile);
//goto Done;
printf(
"%s:%d: error: Data syllable for OBC must be 0 or 2.\r\n",
Input, LineInFile);
ErrorCount++;
s = 0;
}
DataPointer.Module = m;
DataPointer.Page = p;
DataPointer.Syllable = s;
DataPointer.Word = w;
DataPointer.HalfWordMode = HalfWordMode;
HalfWordMode = s ? 1 : 0;
}
if (PassType == PT_CODE)
{
printf(" ");
printf(" \t %s ", Fields[0]);
printf("%o-", m);
printf("%02o-%o-%03o" NL, p, s, w);
}
continue;
}
// Does the line begin with a left-hand symbol or with an operator?
// The difference is that an operator is one of the reserved words.
for (Operator = OP_START; Operator < OP_COUNT; Operator++)
if (!strcmp(Fields[CurrentField], Operators[Operator]))
break;
if (Operator >= OP_COUNT) // Must be a left-hand symbol.
{
LeftHandSymboled = 1;
if (PassType == PT_SYMBOLS)
{
// Could be a duplicate symbol, but I'll do a separate check
// for that later. For now, just go ahead and add it to
// the symbol list.
if (strlen(Fields[CurrentField]) > MAX_SYMSIZE)
{
//fprintf(
// stderr,
// "%s:%d: error: Left-hand symbol %s has too many characters.\n",
// Input, LineInFile, Fields[CurrentField]);
//goto Done;
printf(
"%s:%d: error: Left-hand symbol %s has too many characters.\r\n",
Input, LineInFile, Fields[CurrentField]);
ErrorCount++;
Fields[CurrentField][MAX_SYMSIZE] = 0;
}
if (NumSymbols >= MAXSYMBOLS)
{
printf("%s:%d: error: Max symbol-table size exceeded.\r\n",
Input, LineInFile);
ErrorCount++;
fprintf(stderr,
"%s:%d: error: Max symbol-table size exceeded.\n", Input,
LineInFile);
goto Done;
}
Symbols[NumSymbols].Line = LineTotal;
strcpy(Symbols[NumSymbols].Name, Fields[CurrentField]);
strcpy(Symbols[NumSymbols].RefName, "");
Symbols[NumSymbols].RefType = ST_NONE;
Symbols[NumSymbols].Value = 0xFFFFFFFF; // An invalid value.
// There are a couple of more fields we have to fill in before
// incrementing NumSymbols, but we can't do it quite yet without
// determining the nature of the next input field.
}
CurrentField++;
}
// At this point, we must be out of fields (in which case this is a
// variable allocation) or else must be the name of an opcode or pseudo-op.
// (I arbitrarily disallow comments in variable allocations not having a
// leading #, since otherwise it might be possible for s[] to be the leading
// word of a comment.)
if (CurrentField >= NumFields)
{
if (LeftHandSymboled)
{
SymbolType = ST_VARIABLE;
}
}
else
{
Operatored = 1;
for (Operator = OP_START; Operator < OP_COUNT; Operator++)
if (!strcmp(Fields[CurrentField], Operators[Operator]))
break;
if (Operator < OP_OPCODES)
SymbolType = ST_CODE;
else if (Operator < OP_ALLOCS)
{
SymbolType = ST_CONSTANT;
}
else if (Operator == OP_SYN)
{
SymbolType = ST_SYN;
}
else if (Operator == OP_EQU)
{
SymbolType = ST_EQU;
}
else if (Operator == OP_HOPC)
{
SymbolType = ST_HOPC;
}
else
{
//fprintf(stderr, "%s:%d: error: Unrecognized operator %s.\n",
// Input, LineInFile, Fields[CurrentField]);
//goto Done;
printf("%s:%d: error: Unrecognized operator %s.\r\n", Input,
LineInFile, Fields[CurrentField]);
ErrorCount++;
SymbolType = ST_CODE;
Operator = OP_NOP;
}
}
// Finish making the symbol-table entry, if necessary.
if (LeftHandSymboled && PassType == PT_SYMBOLS)
{
if (SymbolType == ST_CODE)
memcpy(&Symbols[NumSymbols].Address, &InstructionPointer,
sizeof(Address_t));
else
memcpy(&Symbols[NumSymbols].Address, &DataPointer,
sizeof(Address_t));
Symbols[NumSymbols].Type = SymbolType;
NumSymbols++;
}
if (Operatored)
{
CurrentField++; // Move on to operand field, if any.
// Except for NOP, every operator has a single operand,
// so we can check at this point for non-existence (though
// not correctness) of operands, as well as get any
// remaining comment.
if (SymbolType == ST_CODE && Operator == OP_NOP)
/* CurrentField-- */;
else
{
if (CurrentField >= NumFields)
{
//fprintf(stderr, "%s:%d: error: Missing operand for %s.\n",
// Input, LineInFile, Fields[CurrentField - 1]);
//goto Done;
printf("%s:%d: error: Missing operand for %s.\r\n", Input,
LineInFile, Fields[CurrentField - 1]);
ErrorCount++;
SymbolType = ST_CODE;
Operator = OP_NOP;
}
}
if (CurrentField + 1 < NumFields) // Comment?
{
if (Commented)
{
//fprintf(stderr, "%s:%d: error: Garbage after operand: %s.\n",
// Input, LineInFile, FieldStarts[CurrentField + 1]);
//goto Done;
printf("%s:%d: error: Garbage after operand: %s.\r\n", Input,
LineInFile, FieldStarts[CurrentField + 1]);
ErrorCount++;
NumFields = CurrentField + 1;
}
strcpy(Comment, FieldStarts[CurrentField + 1]);
Commented = 1;
}
}
// Perform whatever specific processing is needed for the assembly pass.
switch (SymbolType)
{
case ST_VARIABLE:
// There's really nothing to do here, regardless of what pass this
// is, since a variable allocation does nothing more than advance
// the location pointer.
if (DataPointer.Word > 255)
{
//fprintf(stderr,
// "%s:%d: error: Variable allocation past end of page.\n", Input,
// LineInFile);
//goto Done;
printf("%s:%d: error: Variable allocation past end of page.\r\n",
Input, LineInFile);
ErrorCount++;
DataPointer.Word = 255;
}
if (PassType == PT_CODE)
{
// Write to the listing.
printf("%o-", DataPointer.Module);
//else
// printf(" ");
printf("%02o-%o-%03o ", DataPointer.Page, DataPointer.Syllable,
DataPointer.Word);
printf(" ");
printf("\t%-8s ", Fields[0]);
printf("(ALLOCATION) ");
if (Commented)
printf("\t#%s", Comment);
printf(NL);
}
DataPointer.Word++;
break;
case ST_SYN:
case ST_EQU:
case ST_HOPC:
if (!LeftHandSymboled)
{
//fprintf(
// stderr,
// "%s:%d: error: SYN, EQU, or SYN meaningful only with left-hand symbol.\n",
// Input, LineInFile);
//goto Done;
printf(
"%s:%d: error: SYN, EQU, or SYN meaningful only with left-hand symbol.\r\n",
Input, LineInFile);
ErrorCount++;
break;
}
// The operand is the name of another symbol. We can't resolve
// that just yet, since we don't have a complete symbol table
// to work with. What we'll do right now is simply to record
// in the symbol table that the new symbol refers to the old symbol.
// We'll resolve all these references by processing between the
// 1st pass (PT_SYMBOLS) and the 2nd pass (PT_CODE) and replace them
// with actual OP_VARIABLE, OP_CONSTANT, etc.
if (PassType == PT_SYMBOLS)
strcpy(Symbols[NumSymbols - 1].RefName, Fields[CurrentField]);
if (PassType == PT_CODE)
{
Symbol_t Key, *OurLeftHandSymbol;
strcpy(Key.Name, Fields[0]);
OurLeftHandSymbol = bsearch(&Key, Symbols, NumSymbols,
sizeof(Symbol_t), CompareSymbols);
// Print to listing. The binary was written in between
// the PT_SYMBOLS and PT_CODE.
printf("%o-", OurLeftHandSymbol->Address.Module);
//else
// printf(" ");
printf("%02o-%o-%03o ", OurLeftHandSymbol->Address.Page,
OurLeftHandSymbol->Address.Syllable,
OurLeftHandSymbol->Address.Word);
if (OurLeftHandSymbol->Type == ST_CONSTANT)
{
if (OurLeftHandSymbol->Address.Syllable == 2)
printf(" %05o ", OurLeftHandSymbol->Value);
else
printf("%09o ", OurLeftHandSymbol->Value);
}
else
printf(" ");
printf("\t%-8s ", Fields[0]);
printf("%-4s ", Operators[Operator]);
printf("%-16s ", Fields[CurrentField]);
if (Commented)
printf("\t#%s", Comment);
printf(NL);
}
if (Operator == OP_SYN)
{
// Don't need to increment address pointer here.
}
else if (Operator == OP_EQU || Operator == OP_HOPC)
{
DataPointer.Word++;
}
break;
case ST_CONSTANT:
// In this case, we either have a DEC or OCT pseudo-op.
if (DataPointer.Word > 255)
{
//fprintf(stderr,
// "%s:%d: error: Constant allocation past end of page.\n", Input,
// LineInFile);
//goto Done;
printf("%s:%d: error: Constant allocation past end of page.\r\n",
Input, LineInFile);
ErrorCount++;
DataPointer.Word = 255;
}
switch (Operator)
{
case OP_OCT:
OperandIsInteger = 1;
sss = Fields[CurrentField];
for (; *sss >= '0' && *sss <= '7'; sss++)
;
if (*sss)
{
//fprintf(stderr, "%s:%d: error: Not an octal number: %s.\n",
// Input, LineInFile, Fields[CurrentField]);
//goto Done;
printf("%s:%d: error: Not an octal number: %s.\r\n", Input,
LineInFile, Fields[CurrentField]);
ErrorCount++;
OperandValue = 0;
}
else
sscanf(Fields[CurrentField], "%o", &OperandValue);
if (OperandValue >= (2 << 26))
{
//fprintf(stderr,
// "%s:%d: error: Octal constant out-of-range: %s.\n", Input,
// LineInFile, Fields[CurrentField]);
//goto Done;
printf("%s:%d: error: Octal constant out-of-range: %s.\r\n",
Input, LineInFile, Fields[CurrentField]);
ErrorCount++;
OperandValue = 0;
}
break;
case OP_DEC:
OperandIsInteger = 1;
sss = Fields[CurrentField];
if (*sss == '+' || *sss == '-')
sss++;
for (; isdigit (*sss); sss++)
;
if (*sss)
{
if (*sss == '.')
{
sss++;
OperandIsInteger = 0;
}
for (; isdigit (*sss); sss++)
;
if (*sss)
{
//fprintf(stderr, "%s:%d: error: Not a decimal number: %s.\n",
// Input, LineInFile, Fields[CurrentField]);
//goto Done;
printf("%s:%d: error: Not a decimal number: %s.\r\n", Input,
LineInFile, Fields[CurrentField]);
ErrorCount++;
strcpy(Fields[CurrentField], "0");
}
}
if (HalfWordMode)
i = 12;
else
i = 25;
if (OperandIsInteger)
{
sscanf(Fields[CurrentField], "%d", &OperandValue);
if (OperandValue >= (2 << i) || (OperandValue < -(2 << i)))
{
//fprintf(
// stderr,
// "%s:%d: error: Integer decimal constant out-of-range: %s.\n",
// Input, LineInFile, Fields[CurrentField]);
//goto Done;
printf(
"%s:%d: error: Integer decimal constant out-of-range: %s.\r\n",
Input, LineInFile, Fields[CurrentField]);
ErrorCount++;
strcpy(Fields[CurrentField], "0");
}
}
else
{
sscanf(Fields[CurrentField], "%lf", &fOperandValue);
// Must scale so that 0.5<=|fOperandValue|<1.0. Yes, there
// are probably more-efficient ways to do it. So what?
for (; fabs(fOperandValue) >= 1.0; fOperandValue /= 2.0)
;
if (fOperandValue != 0.0)
for (; fabs(fOperandValue) < 0.5; fOperandValue *= 2.0)
;
OperandValue = lround(fOperandValue * (1 << i));
}
break;
default: // Shouldn't be able to get here.
//fprintf(stderr,
// "%s:%d: error: Implementation error, unparsed constant.\n",
// Input, LineInFile);
//goto Done;
printf("%s:%d: error: Implementation error, unparsed constant.\r\n",
Input, LineInFile);
ErrorCount++;
OperandValue = 0;
}
// Having gotten to here, OperandValue contains a 2's-complement
// value.
OperandValue &= 0x3FFFFFF;
if (LeftHandSymboled && PassType == PT_SYMBOLS)
Symbols[NumSymbols - 1].Value = (unsigned) OperandValue;
if (PassType == PT_CODE)
{
// Write to the binary.
if (WriteBinary(ST_CONSTANT, &DataPointer, OperandValue))
{
//fprintf(stderr, "%s:%d: error: Aborting.\n", Input, LineInFile);
//goto Done;
printf("%s:%d: error: Binary-write error.\r\n", Input,
LineInFile);
ErrorCount++;
}
// Write to the listing.
printf("%o-", DataPointer.Module);
//else
// printf(" ");
printf("%02o-%o-%03o ", DataPointer.Page, DataPointer.Syllable,
DataPointer.Word);
if (DataPointer.Syllable == 2)
printf(" %05o ", OperandValue);
else
printf("%09o ", OperandValue);
if (LeftHandSymboled)
printf("\t%-8s ", Fields[0]);
else
printf("\t ");
printf("%-4s ", Operators[Operator]);
printf("%-16s ", Fields[CurrentField]);
if (Commented)
printf("\t#%s", Comment);
printf(NL);
}
DataPointer.Word++;
break;
case ST_CODE:
if (PassType == PT_SYMBOLS)
{
InstructionPointer.Word++;
break;
}
// Process the individual code types. The opcode is given by
// ParseTypes[Operator].NumericalOpCode, but the operand bits
// need some interpreting. We'll pack the results into 13 bits
// of OperandValue.
if (InstructionPointer.Word > 255)
{
//fprintf(stderr, "%s:%d: error: Code past end of page.\n", Input,
// LineInFile);
//goto Done;
printf("%s:%d: error: Code past end of page.\r\n", Input,
LineInFile);
ErrorCount++;
InstructionPointer.Word = 255;
}
switch (ParseTypes[Operator].OperandType)
{
case OT_YX:
// Here we expect two octal digits. Perhaps later I'll add some
// range-checking on this, since not all values are legal for
// all opcodes accepting this type of operand. For now, I'll
// just sleaze through and accept any values for X and Y.
if (Fields[CurrentField][0] < '0' || Fields[CurrentField][0] > '7'
|| Fields[CurrentField][1] < '0' || Fields[CurrentField][1] > '7'
|| Fields[CurrentField][2] != 0)
{
//fprintf(stderr, "%s:%d: error: Not a legal YX operand.\n", Input,
// LineInFile);
//goto Done;
printf("%s:%d: error: Not a legal YX operand.\r\n", Input,
LineInFile);
ErrorCount++;
strcpy(Fields[CurrentField], "00");
}
sscanf(Fields[CurrentField], "%o", &OperandValue);
break;
case OT_CYX:
{
int Length, Bad;
// Here we expect 2 *or* 3 octal digits. Perhaps later I'll add some
// range-checking on this, since not all values are legal for
// all opcodes accepting this type of operand. For now, I'll
// just sleaze through and accept any values for X and Y. The
// third octal digit, if present, has to be '4'.
Length = strlen(Fields[CurrentField]);
Bad = 0; // Initially, mark as "not bad", then perform tests.
if (Length < 2 || Length > 3)
Bad = 1; // Mark as "bad".
else if (Fields[CurrentField][0] < '0' || Fields[CurrentField][0]
> '7' || Fields[CurrentField][1] < '0'
|| Fields[CurrentField][1] > '7')
Bad = 1;
else if (Length == 3 && Fields[CurrentField][2] != '0'
&& Fields[CurrentField][2] != '4')
Bad = 1;
if (Bad)
{
//fprintf(stderr, "%s:%d: error: Not a legal operand.\n", Input,
// LineInFile);
//goto Done;
printf("%s:%d: error: Not a legal operand.\r\n", Input,
LineInFile);
ErrorCount++;
OperandValue = 0;
}
else
sscanf(Fields[CurrentField], "%o", &OperandValue);
}
break;
case OT_NONE:
OperandValue = 0;
break;
case OT_SHR:
if (!strcmp(Fields[CurrentField], "1"))
OperandValue = 021;
else if (!strcmp(Fields[CurrentField], "2"))
OperandValue = 020;
else
{
//fprintf(stderr, "%s:%d: error: SHR operand must be 1 or 2.\n",
// Input, LineInFile);
//goto Done;
printf("%s:%d: error: SHR operand must be 1 or 2.\r\n", Input,
LineInFile);
ErrorCount++;
OperandValue = 021;
}
break;
case OT_SHL:
if (!strcmp(Fields[CurrentField], "1"))
OperandValue = 030;
else if (!strcmp(Fields[CurrentField], "2"))
OperandValue = 040;
else
{
//fprintf(stderr, "%s:%d: error: SHL operand must be 1 or 2.\n",
// Input, LineInFile);
//goto Done;
printf("%s:%d: error: SHL operand must be 1 or 2.\r\n", Input,
LineInFile);
ErrorCount++;
OperandValue = 030;
}
break;
case OT_NOP:
OperandValue = InstructionPointer.Word + 1;
break;
case OT_ADDRESS:
OperandField = Fields[CurrentField];
// The HOP, CLA, and STO instructions present special difficulties,
// for which we have to perform some pre-processing. Even though
// the operand for HOP is theoretically only a HOP constant, we
// allow it to be a left-hand symbol as well, in which case we
// allocate a HOP constant pointing to that left-hand symbol,
// and of the same name (but enclosed in parentheses) and then
// we substitute that newly-allocated HOP constant for the
// original operand. Similarly, CLA and STO require var/const
// names, but we allow left-hand symbols there as well, allocating
// the HOP-constants as just described.
if (OperandField[0] != '*' && (ParseTypes[Operator].NumericalOpCode
== OP_HOP || ParseTypes[Operator].NumericalOpCode == OP_CLA
|| ParseTypes[Operator].NumericalOpCode == OP_STO))
{
Symbol_t *Result, Key;
char *ss;
// Search for the symbol in the symbol table.
strcpy(Key.Name, OperandField);
Result = bsearch(&Key, Symbols, NumSymbols, sizeof(Symbol_t),
CompareSymbols);
// The next block is to handle the special case in which
// "STO (LHS)" or "CLA (LHS)" has been used prior
// to a "HOP LHS" allocating the constant named
// "(LHS)".
if (Result == NULL && OperandField[0] == '(' && NULL != (ss
= strstr(OperandField, ")")) && ss > &OperandField[1])
{
Symbol_t *Result2, Key2;
strncpy(Key2.Name, &OperandField[1], ss - &OperandField[1]);
Result2 = bsearch(&Key2, Symbols, NumSymbols,
sizeof(Symbol_t), CompareSymbols);
if (Result2 != NULL && Result2->Type == ST_CODE)
{
// So, if we've gotten to this point, the operand was
// "(something)", where "(something)" isn't already
// in the symbol table, but "something" is, and
// furthermore, that "something" is a left-hand symbol.
// If we simply replace the operand by "something",
// we should be able to fall through and let
// the next steps do the work of allocating "(something)".
strcpy(OperandField, Key2.Name);
Result = Result2;
memcpy(&Key, &Key2, sizeof(Key));
}
}
// The next block is the workhorse, executed when the
// operand has been found and is a left-hand symbol
// rather than data as would normally be required for
// HOP, CLA, or STO. What it has to do is to
// allocate "(operand)" (if it does not already exist)
// and doctor the instruction to use "(operand)" in
// place of "operand". The main processing can then
// take it from there.
if (Result != NULL && Result->Type == ST_CODE)
{
Symbol_t *Result2, Key2;
if (strlen(OperandField) > MAX_SYMSIZE - 2)
{
//fprintf(
// stderr,
// "%s:%d: error: In this usage, the LHS length must be %d or less.\n",
// Input, LineInFile, MAX_SYMSIZE - 2);
//goto Done;
printf(
"%s:%d: error: In this usage, the LHS length must be %d or less.\r\n",
Input, LineInFile, MAX_SYMSIZE - 2);
ErrorCount++;
OperandField[MAX_SYMSIZE - 2] = 0;
}
// It's possible that "(operand)" already
// exists, from some prior HOP/STO/CLA.
sprintf(Key2.Name, "(%s)", OperandField);
Result2 = bsearch(&Key2, Symbols, NumSymbols,
sizeof(Symbol_t), CompareSymbols);
if (Result2 == NULL) // Nope, not found.
{
int i, MaxWord = -1, OperandValue;
if (NumSymbols >= MAXSYMBOLS)
{
printf(
"%s:%d: error: Out of symbol space for HOP constant.\r\n",
Input, LineInFile);
ErrorCount++;
fprintf(
stderr,
"%s:%d: error: Out of symbol space for HOP constant.\n",
Input, LineInFile);
goto Done;
}
// Now we have a bit of a chore finding an unused
// address. We basically have to search the entire
// symbol table to find the lowest unused data address
// in the current code sector, and then use that one.
// That guarantees that no variables or constants are
// already stored there, but it doesn't guarantee that
// there won't be a conflict with code that wants to
// go there. Too bad!
for (i = 0; i < NumSymbols; i++)
if (Symbols[i].Address.Page == 017
&& Symbols[i].Address.Syllable == 0)
if (Symbols[i].Address.Word > MaxWord)
MaxWord = Symbols[i].Address.Word;
MaxWord++;
if (MaxWord >= MAX_WORDS)
{
//fprintf(
// stderr,
// "%s:%d: error: Out of space in sector for HOP constant.\n",
// Input, LineInFile);
//goto Done;
printf(
"%s:%d: error: Out of space in sector for HOP constant.\r\n",
Input, LineInFile);
ErrorCount++;
MaxWord--;
}
// In the sprintf below, the input and output strings have the
// same amount of space allocated to them, but the formatting
// adds two characters. The .Name field is sized to accommodate
// this, but the compiler doesn't know this and detects a
// possible overflow. The string 'keyname' is allocated with
// 2 less characters.
{
SymbolName_t keyname = { 0 };
strncpy(keyname, Key.Name, sizeof(keyname) - 1);
sprintf(Symbols[NumSymbols].Name, "(%s)", keyname);
}
Symbols[NumSymbols].Address.HalfWordMode = 0;
Symbols[NumSymbols].Address.Module = 0;
Symbols[NumSymbols].Address.Page = 017;
Symbols[NumSymbols].Address.Syllable = 0;
Symbols[NumSymbols].Address.Word = MaxWord;
Symbols[NumSymbols].Line = LineTotal;
strcpy(Symbols[NumSymbols].RefName, Key.Name);
Symbols[NumSymbols].Type = ST_CONSTANT;
Symbols[NumSymbols].RefType = ST_HOPLHS;
OperandValue = Result->Address.Word & 0377;
if (Result->Address.HalfWordMode)
OperandValue |= 0400000;
OperandValue |= (Result->Address.Syllable & 3) << 14;
OperandValue |= (Result->Address.Page & 0x0F) << 9;
if (Result->Address.Page == RESIDUAL_SECTOR)
OperandValue |= 0400;
Symbols[NumSymbols].Value = OperandValue;
if (WriteBinary(ST_CONSTANT,
&Symbols[NumSymbols].Address,
Symbols[NumSymbols].Value))
{
fprintf(stderr,
"Aborting resolution of \"%s HOPC %s\".\n",
Symbols[i].Name, Symbols[i].RefName);
goto Done;
}
NumSymbols++;
qsort(Symbols, NumSymbols, sizeof(Symbol_t),
CompareSymbols);
}
sprintf(OperandBuffer, "(%s)", OperandField);
OperandField = OperandBuffer;
}
}
// We accept two cases here: Either the operand is of the form
// *+LiteralOctalConstant or *-LiteralOctalConstant, or else it
// is the name of an existing symbol. The former is actually
// possible only if the operand is supposed to be code.
// In the latter case, we must also check if is the
// right type of address (variable/constant vs. code) for the
// opcode type, and whether it's in the current sector vs. the
// residual sector (vs. an unreachable sector).
if (ParseTypes[Operator].AddressType == AT_DATA)
Address = &DataPointer;
else if (ParseTypes[Operator].AddressType == AT_CODE)
Address = &InstructionPointer;
else
{
//fprintf(stderr, "%s:%d: error: Implementation error.\n", Input,
// LineInFile);
//goto Done;
printf("%s:%d: error: Implementation error.\r\n", Input,
LineInFile);
ErrorCount++;
Address = &DataPointer;
}
if (OperandField[0] == '*' && ParseTypes[Operator].AddressType
== AT_CODE)
{
// Could do some checking here for garbage at the end, but
// am too lazy.
if (OperandField[1] == 0)
OperandValue = Address->Word;
else if (OperandField[1] == '+' && 1 == sscanf(&OperandField[2],
"%o", &OperandValue))
OperandValue = Address->Word + OperandValue;
else if (OperandField[1] == '-' && 1 == sscanf(&OperandField[2],
"%o", &OperandValue))
OperandValue = Address->Word - OperandValue;
else
{
//fprintf(stderr,
// "%s:%d: error: Illegal relative addressing.\n", Input,
// LineInFile);
//goto Done;
printf("%s:%d: error: Illegal relative addressing.\r\n",
Input, LineInFile);
ErrorCount++;
OperandValue = Address->Word;
}
if (OperandValue < 0 || OperandValue > 255)
{
//fprintf(stderr,
// "%s:%d: error: Relative addressing past end of page.\n",
// Input, LineInFile);
//goto Done;
printf(
"%s:%d: error: Relative addressing past end of page.\r\n",
Input, LineInFile);
ErrorCount++;
OperandValue = (OperandValue < 0) ? 0 : 255;
}
}
else
{
Symbol_t *Result, Key, Dummy =
{ 0 };
// Search for the symbol in the symbol table.
strcpy(Key.Name, OperandField);
Result = bsearch(&Key, Symbols, NumSymbols, sizeof(Symbol_t),
CompareSymbols);
if (Result == NULL)
{
//fprintf(stderr,
// "%s:%d: error: Symbol used as operand not found.\n",
// Input, LineInFile);
//goto Done;
printf("%s:%d: error: Symbol used as operand not found.\r\n",
Input, LineInFile);
ErrorCount++;
Result = &Dummy;
}
if ((ParseTypes[Operator].AddressType == AT_CODE && Result->Type
!= ST_CODE)
|| (ParseTypes[Operator].AddressType == AT_DATA
&& Result->Type != ST_VARIABLE && Result->Type
!= ST_CONSTANT))
{
//fprintf(
// stderr,
// "%s:%d: error: Operand symbol is wrong type (code vs. data).\n",
// Input, LineInFile);
//goto Done;
printf(
"%s:%d: error: Operand symbol is wrong type (code vs. data).\r\n",
Input, LineInFile);
ErrorCount++;
}
// Need to do some checking on syllable matches.
if (HalfWordMode)
{
//fprintf(stderr, "%s:%d: error: Half-word mode not implemented yet.\n", Input, LineInFile);
//goto Done;
printf(
"%s:%d: error: Half-word mode not implemented yet.\r\n",
Input, LineInFile);
ErrorCount++;
}
else
{
if (Result->Type == ST_VARIABLE || Result->Type
== ST_CONSTANT)
{
if (Result->Address.Syllable != 0)
{
//fprintf(
// stderr,
// "%s:%d: error: Not in half-word mode, cannot access syllable 2 data.\n",
// Input, LineInFile);
//goto Done;
printf(
"%s:%d: error: Not in half-word mode, cannot access syllable 2 data.\r\n",
Input, LineInFile);
ErrorCount++;
}
}
else
{
if (Result->Address.Syllable != Address->Syllable)
{
//fprintf(
// stderr,
// "%s:%d: error: Cannot TRA, TMI, or TNZ to code in different syllable.\n",
// Input, LineInFile);
//goto Done;
printf(
"%s:%d: error: Cannot TRA, TMI, or TNZ to code in different syllable.\r\n",
Input, LineInFile);
ErrorCount++;
}
}
}
OperandValue = Result->Address.Word;
if (Result->Address.Page == RESIDUAL_SECTOR)
OperandValue |= 0400; // Set residual sector.
else if (Result->Address.Page != Address->Page)
{
//fprintf(
// stderr,
// "%s:%d: error: Operand symbol is in an inaccessible page.\n",
// Input, LineInFile);
//goto Done;
printf(
"%s:%d: error: Operand symbol is in an inaccessible page.\r\n",
Input, LineInFile);
ErrorCount++;
}
}
break;
default:
//fprintf(stderr, "%s:%d: error: Implementation error.\n", Input,
// LineInFile);
//goto Done;
printf("%s:%d: error: Implementation error.\r\n", Input, LineInFile);
ErrorCount++;
OperandValue = 0;
}
// Okay, OperandValue should now hold the A1-A9 field, so let's fill in the
// opcode field.
OperandValue |= (ParseTypes[Operator].NumericalOpCode << 9);
if (WriteBinary(ST_CODE, &InstructionPointer, OperandValue))
{
//fprintf(stderr, "%s:%d: error: Aborting.\n", Input, LineInFile);
//goto Done;
printf("%s:%d: error: Write-binary error.\r\n", Input, LineInFile);
ErrorCount++;
}
printf("%o-", InstructionPointer.Module);
printf("%02o-%o-%03o ", InstructionPointer.Page,
InstructionPointer.Syllable, InstructionPointer.Word);
printf(" %05o ", OperandValue);
if (LeftHandSymboled)
printf("\t%-8s ", Fields[0]);
else
printf("\t ");
printf("%-4s ", Operators[Operator]);
printf("%-16s ", Fields[CurrentField]);
if (Commented)
printf("\t#%s", Comment);
printf(NL);
InstructionPointer.Word++;
break;
default: // I don't think this can happen.
//fprintf(stderr, "%s:%d: error: Implementation error, unparsed line.\n",
// Input, LineInFile);
//goto Done;
printf("%s:%d: error: Implementation error, unparsed line.\r\n", Input,
LineInFile);
ErrorCount++;
}
}
RetVal = 0;
Done: ;
return (RetVal);
}
Computing file changes ...