swh:1:snp:63e2d142f91fc04ec33789d9d7bb85f3bef72e05
Tip revision: 66d8e606a8d996ded60bc81d5edf319142a5fad9 authored by Ron Burkey on 04 October 2021, 11:49:55 UTC
Merge branch 'master' of https://github.com/virtualagc/virtualagc
Merge branch 'master' of https://github.com/virtualagc/virtualagc
Tip revision: 66d8e60
gdbInterface.c
/*
* Copyright 2020 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
*
* In addition, as a special exception, Ronald S. Burkey gives permission to
* link the code of this program with the Orbiter SDK library (or with
* modified versions of the Orbiter SDK library that use the same license as
* the Orbiter SDK library), and distribute linked combinations including
* the two. You must obey the GNU General Public License in all respects for
* all of the code used other than the Orbiter SDK library. If you modify
* this file, you may extend this exception to your version of the file,
* but you are not obligated to do so. If you do not wish to do so, delete
* this exception statement from your version.
*
* Filename: gdbInterface.c
* Purpose: Provides a debugger interface for the LVDC/PTC emulator.
* in spite of the name, it's not necessarily a gdb-like
* interface, but I already have other functions and filenames
* with names like debug*, so I thought it would be prudent
* to name it more distinctly.
* Compiler: GNU gcc.
* Reference: http://www.ibibio.org/apollo
* Mods: 2020-04-30 RSB Began.
* 2020-05-21 RSB Fixed arguments for DISASSEMBLE.
*/
#include <stdlib.h>
#include <string.h>
#include <stdio.h>
#include <ctype.h>
#include "yaLVDC.h"
////////////////////////////////////////////////////////////////////////////////
// Utility functions.
// Create a descriptive string for a HOP constant, specified either by giving
// the constant itself, or else by module/sector/location if the constant is given
// as -1. A char buffer[32] to store the string is one of the arguments.
// Returns 0 if successful, 1 otherwise. The string is still created in
// case of failure.
static int
formHopDescription(int hopConstant, int module, int sector, int location,
char *buffer, hopStructure_t *hs)
{
if (hopConstant == -1)
{
if (module < 0 || module > 7 || sector < 0 || sector > 017 || location < 0
|| location > 0377)
{
sprintf(buffer, "????????? (%-19s)", "illegal address");
return (1);
}
if (state.core[module][sector][2][location] == -1)
{
sprintf(buffer, "????????? (%-19s)", "empty address");
return (1);
}
hopConstant = state.core[module][sector][2][location];
}
if (parseHopConstant(hopConstant, hs))
{
sprintf(buffer, "%09o (%-19s)", hopConstant, "invalid HOP constant");
return (1);
}
sprintf(buffer, "%09o (ADR=%o-%02o-%o-%03o/%o-%02o)", hopConstant, hs->im,
hs->is, hs->s, hs->loc, hs->dm, hs->ds);
return (0);
}
// Parse an input line into fields. The first field is the command, and it
// is any number of alphabetic characters, upper or lower case, and is
// automatically converted to upper case. The second field, if any,
// needn't be separated from the first one if its 1st character is non-alphabetic.
// Otherwise, all fields are delimited by whitespace. The return value is
// the number of fields found, or -1 if error.
#define MAX_COMMAND_FIELDS 10
#define MAX_COMMAND_LENGTH 128
static char lineBuffer[MAX_COMMAND_LENGTH],
fields[MAX_COMMAND_FIELDS][MAX_COMMAND_LENGTH],
lastLine[MAX_COMMAND_LENGTH];
int
parseCommand(void)
{
int numFields = 0;
char *s, *ss;
// Get rid of any leading whitespace.
for (s = lineBuffer; *s && isspace(*s); s++)
;
if (!*s)
goto done;
// Parse first field. Note that it cannot be longer than fields[0], since
// fields[0] is itself as large as the input line buffer.
for (ss = fields[numFields]; *s && isalpha(*s); s++, ss++)
*ss = toupper(*s);
*ss = 0;
numFields++;
// Now parse all remaining fields.
while (*s)
{
// Get rid of any leading whitespace.
for (; *s && isspace(*s); s++)
;
if (!*s)
goto done;
// Again, fields[numFields] is the same length as lineBuffer,
// so overflow is impossible.
for (ss = fields[numFields]; *s && !isspace(*s); s++, ss++)
*ss = *s;
*ss = 0;
numFields++;
}
done: ;
return (numFields);
}
// Associate command strings with command tokens. A token is returned
// for the first valid command for which the input command is a leading
// substring. For example, ctSTEP is returned if the input command
// is S, ST, STE, or STEP. The token ctNone is returned if the input
// command doesn't match anything. Because these abbreviations may not
// be unique, the commandAssociations[] array is ordered in terms of
// priority for the abbreviations. For example, "STEP" comes first
// because it's more-important than other commands beginning with "S",
// and we want to make sure that "S" is an abbreviation for "STEP" rather
// than some other S-command.
enum commandTokens
{
ctSTEP,
ctNEXT,
ctDELETE,
ctCONTINUE,
ctJUMP,
ctGOTO,
ctLIST,
ctDISASSEMBLE,
ctX,
ctCLEAR,
ctBREAK,
ctTBREAK,
ctBACKTRACE,
ctINFO,
ctRUN,
ctQUIT,
ctSET,
ctSHOW,
ctPANEL,
ctHELP,
ctNone
};
typedef struct
{
enum commandTokens token;
const char *string;
const char *syntax;
const char *description;
} commandAssociation_t;
commandAssociation_t commandAssociations[] =
{
{ ctSTEP, "STEPI", "STEPI [n]", "Step n instructions, default n=1." },
{ ctSTEP, "STEPI", "SI [n]", "Same as STEPI." },
{ ctNEXT, "NEXTI", "NEXTI [n]", "Next n instructions, w/o entry." },
{ ctNEXT, "NEXTI", "NI [n]", "Same as NEXTI." },
{ ctDELETE, "DELETE", "DELETE", "Delete all breakpoints." },
{ ctDELETE, "DELETE", "DELETE n", "Delete breakpoint n." },
{ ctCONTINUE, "CONTINUE", "CONTINUE", "Continue running emulation." },
{ ctJUMP, "JUMP", "JUMP *address M-SS",
"Jump to code-memory address and assign DM/DS as M-SS and run." },
{ ctJUMP, "JUMP", "JUMP [asm:]name",
"Jump. Operand is symbolic name of a HOP constant in data memory." },
{ ctJUMP, "JUMP", "JUMP [asm:]name M-SS",
"Jump to symbol in code memory and assign DM/DS as M-SS." },
{ ctJUMP, "JUMP", "JUMP octal",
"Jump using literal octal HOP constant." },
{ ctGOTO, "GOTO", "GOTO ...",
"Same as JUMP, except pause rather than run." },
{ ctLIST, "LIST", "LIST", "List following block of source code." },
{ ctLIST, "LIST", "LIST -", "List preceding block source code." },
{ ctLIST, "LIST", "LIST [asm:]n", "List source block at line #n." },
{ ctLIST, "LIST", "LIST [asm:]name",
"List source block at function." },
{ ctDISASSEMBLE, "DISASSEMBLE", "DISASSEMBLE",
"Disassemble next LISTSIZE instructions in current HOP environment." },
{ ctDISASSEMBLE, "DISASSEMBLE", "DISASSEMBLE - D-TT",
"Disassemble preceding LISTSIZE instructions; D-TT is starting DM/DS." },
{ ctDISASSEMBLE, "DISASSEMBLE", "DISASSEMBLE M-SS-Y-LLL EEE D-TT",
"Disassemble from code address M-SS-Y-LLL to EEE using data sector D-TT." },
{ ctX, "X", "X[/[n][b|d|o]] address", "Show n words at address." },
{ ctX, "X", "X[/[n][b|d|o]] &[asm:]name", "Show n words at name." },
{ ctCLEAR, "CLEAR", "CLEAR [asm:]name",
"Delete breakpoint at function." },
{ ctCLEAR, "CLEAR", "CLEAR [asm:]number",
"Delete breakpoint at line #." },
{ ctCLEAR, "CLEAR", "CLEAR *address",
"Delete breakpoint at address." },
{ ctBREAK, "BREAK", "BREAK [asm:]name",
"Set breakpoint at function." },
{ ctBREAK, "BREAK", "BREAK [asm:]number",
"Set breakpoint at line #." },
{ ctBREAK, "BREAK", "BREAK *address", "Set breakpoint at address." },
{ ctTBREAK, "TBREAK", "TBREAK ...", "Same as BREAK, but temporary." },
{ ctBACKTRACE, "BACKTRACE", "BACKTRACE [n]",
"Show last n backtraces, default=20." },
{ ctBACKTRACE, "BACKTRACE", "BT [n]", "Same as BACKTRACE." },
{ ctINFO, "INFO", "INFO ASSEMBLIES", "List all loaded assemblies." },
{ ctINFO, "INFO", "INFO BREAKPOINTS", "List all breakpoints." },
{ ctINFO, "INFO", "INFO BREAK", "List all breakpoint numbers." },
{ ctRUN, "RUN", "RUN", "Reboot the LVDC/PTC emulation." },
{ ctQUIT, "QUIT", "QUIT", "Quit the LVDC/PTC emulator." },
{ ctSET, "SET", "SET LISTSIZE n", "Sets default size used for LIST." },
{ ctSET, "SET", "SET name = n", "Store value n in variable name." },
{ ctSET, "SET", "SET *address = n",
"Store value n at memory address." },
{ ctSHOW, "SHOW", "SHOW LISTSIZE", "Show current size for LIST." },
{ ctPANEL, "PANEL", "PANEL",
"Resume full control by PTC front panel." },
{ ctHELP, "HELP", "HELP", "Print this list of commands." },
{ ctNone, "" } };
enum commandTokens
findCommandToken(void)
{
int i, n = strlen(fields[0]);
for (i = 0; commandAssociations[i].token != ctNone; i++)
{
if (!strncasecmp(fields[0], commandAssociations[i].string, n))
break;
}
return (commandAssociations[i].token);
}
// Find the assembly, given its name (from the --assembly switch).
// The return value is NULL if there is no match.
assembly_t *
findAssemblyByName(char *assemblyName)
{
int i;
if (numAssemblies <= 0 || assemblyName == NULL)
return (NULL);
for (i = 0; i < numAssemblies; i++)
if (!strcmp(assemblies[i].name, assemblyName))
return (&assemblies[i]);
return (NULL);
}
// Find a name in the symbol table. Accepts names of the form assembly:symbol
// or just symbol. Returns a pointer to the symbol-table entry or to NULL
// if not found. If the input assemblyName is missing, then all
// assemblies are searched, but only the first match found is returned.
assembly_t *symbolAssembly;
symbol_t *
findSymbol(char *symbolName, int code)
{
int i, start = 0, end = numAssemblies;
char *assemblyName = NULL, *ss;
assembly_t *assembly = assemblies;
symbol_t *symbol = NULL;
symbolAssembly = NULL;
ss = strstr(symbolName, ":");
if (ss != NULL)
{
*ss = 0;
assemblyName = symbolName;
symbolName = ss + 1;
}
if (assemblyName != NULL)
{
assembly = findAssemblyByName(assemblyName);
*ss = ':';
if (assembly == NULL)
{
printf("No such assembly.\n");
return (NULL);
}
start = assembly - assemblies;
end = start + 1;
}
for (; start < end; start++, assembly++)
for (i = 0; i < assembly->numSymbols; i++)
if (!strcasecmp(symbolName, assembly->symbols[i].name)
&& ((code && assembly->symbols[i].syllable < 2)
|| (!code && assembly->symbols[i].syllable == 2)))
{
if (symbol == NULL)
{
symbol = &assembly->symbols[i];
symbolAssembly = assembly;
}
else
printf("Symbol specification is not unique.\n");
}
return (symbol);
}
// Find an address in the symbol table. Returns a pointer to
// the symbol-table entry or to NULL if not found. The assembly
// in which it was found is in the global symbolAssembly variable.
symbol_t *
findSymbolByAddress(int module, int sector, int syllable, int location)
{
int i, j;
assembly_t *assembly;
symbol_t *symbol;
for (j = 0, assembly = assemblies; j < numAssemblies; j++, assembly++)
for (i = 0, symbol = assembly->symbols; i < assembly->numSymbols;
i++, symbol++)
if (module == symbol->module && sector == symbol->sector
&& syllable == symbol->syllable && location == symbol->loc)
{
symbolAssembly = assembly;
return (symbol);
}
symbolAssembly = NULL;
return (NULL);
}
symbol_t *
findSymbolByDataAddress(int module, int sector, int residual, int location)
{
if (!residual)
return (findSymbolByAddress(module, sector, 2, location));
else if (ptc)
return (findSymbolByAddress(0, 017, 2, location));
else
return (findSymbolByAddress(module, 017, 2, location));
}
// Find a line number in the source table. Return a pointer to the source-table
// entry or NULL if not found. The lineNumber string can include an assembly
// name.
assembly_t *lineNumberAssembly;
sourceLine_t *
findLineNumber(char *lineNumberString)
{
int i, start = 0, end = numAssemblies, lineNumber;
char *ss, *assemblyName = NULL;
assembly_t *assembly = assemblies;
sourceLine_t *sourceLine = NULL;
lineNumberAssembly = NULL;
ss = strstr(lineNumberString, ":");
if (ss != NULL)
{
*ss = 0;
assemblyName = lineNumberString;
lineNumber = atoi(ss + 1);
*ss = ':';
}
else
lineNumber = atoi(lineNumberString);
if (assemblyName != NULL)
{
assembly = findAssemblyByName(assemblyName);
if (assembly == NULL)
{
printf("No such assembly.\n");
return (NULL);
}
start = assembly - assemblies;
end = start + 1;
}
for (; start < end; start++, assembly++)
for (i = 0; i < assembly->numSourceLines; i++)
if (assembly->sourceLines[i].lineNumber == lineNumber)
{
if (sourceLine == NULL)
{
sourceLine = &assembly->sourceLines[i];
lineNumberAssembly = assembly;
}
else
printf("Source line-number specification is not unique.\n");
}
return (sourceLine);
}
// Print block of source-code lines. The start and end parameters
// are not line numbers, but are indexes into the sourceLine_t array.
void
printSourceBlock(assembly_t *assembly, int start, int end)
{
int i, j;
int module, sector, syllable, location, assembled, dm, ds;
sourceLine_t *sourceLine;
char c;
breakpoint_t *breakpoint;
if (start < 0)
start = 0;
if (end > assembly->numSourceLines)
end = assembly->numSourceLines;
printf("Assembly %s:\n", assembly->name);
for (i = start, sourceLine = &assembly->sourceLines[start]; i < end;
i++, sourceLine++)
{
module = sourceLine->module;
sector = sourceLine->sector;
syllable = sourceLine->syllable;
location = sourceLine->loc;
assembled = sourceLine->assembled;
dm = sourceLine->dm;
ds = sourceLine->ds;
c = ' ';
for (j = 0, breakpoint = breakpoints; j < numBreakpoints;
j++, breakpoint++)
if (module == breakpoint->module && sector == breakpoint->sector
&& syllable == breakpoint->syllable
&& location == breakpoint->location)
{
c = '*';
break;
}
printf("%6d:%c %o-%02o-%o-%03o %o-%02o %05o %s\n",
assembly->sourceLines[i].lineNumber, c, module, sector, syllable,
location, dm, ds, assembled, assembly->sourceLines[i].line);
}
}
// Parse an input address string.
enum addressType_t
{
atCode, atData, atPio, atCio, atPrs, atNone
};
enum addressType_t
parseInputAddress(char *string, int *module, int *sector, int *syllable,
int *location)
{
char s[256];
//if (!strcasecmp(string, "PRS"))
// return (atPrs);
if (4
== sscanf(string, "%o-%o-%o-%o%s", module, sector, syllable, location, s))
return (atCode);
if (3 == sscanf(string, "%o-%o-%o%s", module, sector, location, s))
return (atData);
if (strlen(string) > 3 && 1 == sscanf(&string[3], "-%o%s", location, s))
{
if (!strncasecmp(string, "PIO", 3))
return (atPio);
else if (!strncasecmp(string, "CIO", 3))
return (atCio);
else if (!strncasecmp(string, "PRS", 3))
return (atPrs);
}
return (atNone);
}
// Convert an integer to binary. The output buffer must be
// padTo+1 bytes or longer. Returns 0 on success or 1 on failure.
int
toBinary(int value, int padTo, char *buffer)
{
buffer[padTo] = 0;
while (padTo > 0)
{
padTo--;
if ((value & 1) == 0)
buffer[padTo] = '0';
else
buffer[padTo] = '1';
value = value >> 1;
}
return (value != 0);
}
// Format a value fetched from code or data memory, or PIO/CIO port.
char formattedFetchedValue[64];
void
formatFetchedValue(int value, char type /* o, d, or b */,
int size /* 0 for code, 1 or anything else */, int syllable /* 0, 1, 2 */)
{
strcpy(formattedFetchedValue, "unknown");
if (value == -1 && size)
sprintf(formattedFetchedValue, "empty data location");
else if (value == -1 && !size)
sprintf(formattedFetchedValue, "empty code location");
else if (type == 'o' && size)
sprintf(formattedFetchedValue, "%09o (%09o)", value, value << 1);
else if (type == 'o' && !size)
sprintf(formattedFetchedValue, "%05o (%05o)", value,
value << (syllable ? 2 : 1));
else if (type == 'd')
{
if (size && (value & 0200000000) != 0)
value = -(value & 0177777777);
sprintf(formattedFetchedValue, "%d", value);
}
else if (type == 'b')
{
int numBits = 26;
if (!size)
numBits = 13;
toBinary(value, numBits, formattedFetchedValue);
}
}
assembly_t *assemblySourceLineByAddress;
int
findSourceLineByAddress(int module, int sector, int syllable, int location)
{
assemblySourceLineByAddress = NULL;
// Search for the matching source line. We check both the spec'd
// syllable and syllable "2" (a data word spanning both syllables
// 0 and 1) for emptiness, because the self-modifying nature of the
// code sometimes blurs the lines.
if (state.core[module][sector][syllable][location] != -1
|| state.core[module][sector][2][location] != -1)
{
int i;
assembly_t *assembly;
int assemblyNumber;
for (assemblyNumber = 0, assembly = assemblies;
assemblyNumber < numAssemblies; assemblyNumber++, assembly++)
{
for (i = 0; i < assembly->numSourceLines; i++)
{
if (assembly->sourceLines[i].module != module)
continue;
if (assembly->sourceLines[i].sector != sector)
continue;
if (assembly->sourceLines[i].syllable != syllable)
continue;
if (assembly->sourceLines[i].loc != location)
continue;
assemblySourceLineByAddress = assembly;
return (i);
}
}
}
return (-1);
}
// The disassembly process works as follows. First, setupDisassembly()
// is used to setup up the state of the process properly. It can be
// set up either with a hopStructure_t, or else (if the pointer to the
// structure is NULL), a HOP constant. That setup sets the instruction
// sector, the starting instruction address, and the starting data
// sector. Actually performing the dissasembly is to successively
// use the disassemble() function, which not only disassembles the
// current instruction, but also sets the state properly for the next
// instruction. The range of instructions disassembled has to reside
// in a single sector.
struct
{
int valid;
int error;
int im, is, s, loc, dm, ds;
} disassemblyState =
{ 0 };
int
setupDisassembly(uint32_t hop, hopStructure_t *hs)
{
hopStructure_t hsl;
disassemblyState.valid = 0;
disassemblyState.error = 0;
if (hs == NULL)
{
hs = &hsl;
if (parseHopConstant(hop, hs))
return (1);
}
else
{
memcpy(&hsl, hs, sizeof(hopStructure_t));
hs = &hsl;
}
if (hs->im < 0 || hs->im > 7)
return (1);
disassemblyState.im = hs->im;
if (hs->is < 0 || hs->is > 017)
return (1);
disassemblyState.is = hs->is;
if (hs->s < 0 || hs->s > 1)
return (1);
disassemblyState.s = hs->s;
if (hs->loc < 0 || hs->loc > 0377)
return (1);
disassemblyState.loc = hs->loc;
if (hs->dm < 0 || hs->dm > 7)
return (1);
disassemblyState.dm = hs->dm;
if (hs->ds < 0 || hs->ds > 017)
return (1);
disassemblyState.ds = hs->ds;
disassemblyState.valid = 1;
return (0);
}
char *
disassemble(void)
{
static char lineBuffer[256];
char *opcode, *lhs, operandString[128];
uint16_t instruction;
uint8_t op, operand, a8, a9, a91;
hopStructure_t hsc;
symbol_t *lhsSymbol = NULL, *operandSymbol = NULL;
int isHopOperand = 0, isUsualOperand = 0, isTraOperand = 0, isLiteralOperand =
0, originalInhibit = inhibitFetchMessages;
int m = 0, n = 0;
inhibitFetchMessages = 1;
if (!disassemblyState.valid)
{
m += sprintf(&lineBuffer[m], "(disassembly process not initialized)");
goto error;
}
m += sprintf(&lineBuffer[m], "%o-%02o-%o-%03o %o-%02o ", disassemblyState.im,
disassemblyState.is, disassemblyState.s, disassemblyState.loc,
disassemblyState.dm, disassemblyState.ds);
if (disassemblyState.loc > 0377)
{
m += sprintf(&lineBuffer[m], "(address past end of sector)");
goto error;
}
lhsSymbol = findSymbolByAddress(disassemblyState.im, disassemblyState.is,
disassemblyState.s, disassemblyState.loc);
if (lhsSymbol == NULL)
lhs = "";
else
lhs = lhsSymbol->name;
if (fetchInstruction(disassemblyState.im, disassemblyState.is,
disassemblyState.s, disassemblyState.loc, &instruction,
&instructionFromDataMemory))
{
m += sprintf(&lineBuffer[m], "(no instruction at current address)");
goto error;
}
m += sprintf(&lineBuffer[m], "%05o ", instruction);
// Parse instruction into fields.
op = instruction & 017;
a9 = (instruction >> 4) & 1;
operand = (instruction >> 5) & 0377;
a8 = (operand >> 7) & 1;
a91 = (a9 << 8) | operand;
// Execute the instruction.
if (op == 000)
{
opcode = "HOP";
isHopOperand = 1;
}
else if (!ptc && op == 001)
{
opcode = "MPY";
isUsualOperand = 1;
}
else if (!ptc && op == 005)
{
opcode = "MPH";
isUsualOperand = 1;
}
else if (ptc && op == 001)
{
opcode = "PRS";
isLiteralOperand = 1;
}
else if (ptc && op == 005)
{
opcode = "CIO";
isLiteralOperand = 1;
}
else if (op == 002)
{
opcode = "SUB";
isUsualOperand = 1;
}
else if (!ptc && op == 003)
{
opcode = "DIV";
isUsualOperand = 1;
}
else if (op == 004)
{
opcode = "TNZ";
isTraOperand = 1;
}
else if (op == 006)
{
opcode = "AND";
isUsualOperand = 1;
}
else if (op == 007)
{
opcode = "ADD";
isUsualOperand = 1;
}
else if (op == 010)
{
opcode = "TRA";
isTraOperand = 1;
}
else if ((!ptc && op == 011) || (ptc && op == 015))
{
opcode = "XOR";
isUsualOperand = 1;
}
else if (op == 012)
{
opcode = "PIO";
isLiteralOperand = 1;
}
else if (op == 013)
{
opcode = "STO";
isUsualOperand = 1;
}
else if (op == 014)
{
opcode = "TMI";
isTraOperand = 1;
}
else if ((!ptc && op == 015) || (ptc && op == 003))
{
opcode = "RSU";
isUsualOperand = 1;
}
else if (ptc && op == 016 && a8 == 1)
{
opcode = "CDS";
disassemblyState.dm = (operand >> 4) & 1;
disassemblyState.ds = operand & 0xF;
sprintf(operandString, "%o,%o", disassemblyState.dm, disassemblyState.ds);
}
else if (!ptc && op == 016 && a8 == 0 && a9 == 0)
{
opcode = "CDS";
disassemblyState.dm = (operand >> 1) & 7;
disassemblyState.ds = (operand >> 4) & 0xF;
sprintf(operandString, "%o,%o", disassemblyState.dm, disassemblyState.ds);
}
else if (ptc && op == 016 && a8 == 0)
{
if ((operand & 0100) == 0)
opcode = "SHL";
else
opcode = "SHR";
switch (operand & 077)
{
case 001:
strcpy(operandString, "1");
break;
case 002:
strcpy(operandString, "2");
break;
case 004:
strcpy(operandString, "3");
break;
case 010:
strcpy(operandString, "4");
break;
case 020:
strcpy(operandString, "5");
break;
case 030:
strcpy(operandString, "6");
break;
default:
strcpy(operandString, "(illegal value)");
break;
}
}
else if (!ptc && op == 016 && a8 == 0 && a9 == 1)
{
switch (operand)
{
case 000:
opcode = "SHF";
strcpy(operandString, "0");
break;
case 001:
opcode = "SHR";
strcpy(operandString, "1");
break;
case 002:
opcode = "SHR";
strcpy(operandString, "2");
break;
case 020:
opcode = "SHL";
strcpy(operandString, "1");
break;
case 040:
opcode = "SHL";
strcpy(operandString, "2");
break;
default:
opcode = "SHF";
strcpy(operandString, "(illegal value)");
break;
}
}
else if (!ptc && op == 016 && a8 == 1 && a9 == 1)
{
opcode = "EXM";
sprintf(operandString, "%o,%o,%o", (instruction >> 4) & 7,
(operand >> 4) & 1, operand & 0xF);
}
else if (op == 017)
{
opcode = "CLA";
isUsualOperand = 1;
}
else
{
sprintf(lineBuffer, "(cannot disassemble value %05o)", instruction);
goto error;
}
// Additional formatting of operand, by instruction type.
if (isHopOperand)
{
symbol_t *operandSymbol = NULL, *hopSymbol = NULL;
int hopValueMissing = 1, hopValue = -1, hopValueCorrupt = 1;
operandSymbol = findSymbolByDataAddress(disassemblyState.dm,
disassemblyState.ds, a9, operand);
hopValueMissing = fetchData(disassemblyState.dm, a9, disassemblyState.ds,
operand, &hopValue, &dataFromInstructionMemory);
if (!hopValueMissing)
{
hopValueCorrupt = parseHopConstant(hopValue, &hsc);
if (!hopValueCorrupt)
hopSymbol = findSymbolByAddress(hsc.im, hsc.is, hsc.s, hsc.loc);
}
// To format the output operand, work downward from
// most-preferable case to least-preferable case.
n = 0;
if (hopSymbol != NULL)
n += sprintf(&operandString[n], "%s (destination), ", hopSymbol->name);
else if (!hopValueCorrupt)
n += sprintf(&operandString[n], "%o-%02o-%o-%03o (destination), ",
hsc.im, hsc.is, hsc.s, hsc.loc);
else if (hopValueMissing)
n += sprintf(&operandString[n], "Missing HOP value, ");
else
n += sprintf(&operandString[n], "Corrupt HOP value, ");
if (operandSymbol != NULL)
n += sprintf(&operandString[n], "%s (operand)", operandSymbol->name);
else if (a9 == 0)
n += sprintf(&operandString[n], "%o-%02o-%03o (operand)",
disassemblyState.dm, disassemblyState.ds, operand);
else if (ptc)
n += sprintf(&operandString[n], "0-17-%03o (operand)", operand);
else
n += sprintf(&operandString[n], "%o-17-%03o (operand)",
disassemblyState.dm, operand);
}
else if (isUsualOperand)
{
int value;
// The "usual operand" is a data location, possibly in the residual sector.
operandSymbol = findSymbolByDataAddress(disassemblyState.dm,
disassemblyState.ds, a9, operand);
n = 0;
if (operandSymbol != NULL)
{
char *prefix;
if (isdigit(operandSymbol->name[0]))
prefix = "=O";
else
prefix = "";
n += sprintf(&operandString[n], "%s%s", prefix, operandSymbol->name);
}
else if (a9 == 0)
n += sprintf(&operandString[n], "%o-%02o-%03o", disassemblyState.dm,
disassemblyState.ds, operand);
else if (ptc)
n += sprintf(&operandString[n], "0-17-%03o", operand);
else
n += sprintf(&operandString[n], "%o-17-%03o", disassemblyState.dm,
operand);
if (fetchData(disassemblyState.dm, a9, disassemblyState.ds, operand,
&value, &dataFromInstructionMemory))
n += sprintf(&operandString[n], ", memory location is empty");
else
n += sprintf(&operandString[n], ", stored value = %09o", value);
}
else if (isTraOperand)
{
operandSymbol = findSymbolByAddress(disassemblyState.is,
disassemblyState.im, a9, operand);
if (operandSymbol == NULL)
sprintf(operandString, "%o-%0o-%o-%03o", disassemblyState.im,
disassemblyState.is, a9, operand);
else
strcpy(operandString, operandSymbol->name);
}
else if (isLiteralOperand)
{
sprintf(operandString, "%03o", a91);
}
m += sprintf(&lineBuffer[m], "%-8s%-8s%s", lhs, opcode, operandString);
if (0)
{
error: ;
disassemblyState.error = 1;
}
disassemblyState.loc++;
inhibitFetchMessages = originalInhibit;
return (lineBuffer);
}
////////////////////////////////////////////////////////////////////////////////
// The top-level function in this file. It assumes that the LVDC/PTC program
// has been paused. Information about the current register values is printed,
// and a command is accepted from the user.
// The return value is 0 on success, non-zero on failure.
int
gdbInterface(unsigned long instructionCount, unsigned long cycleCount,
breakpoint_t *breakpoint)
{
int retVal = 1;
hopStructure_t hs, hs2;
char hopBuffer[32];
size_t count;
int value, lineIndexInAssembly;
static int listSize = 20;
// runNextN is a global variable telling the LVDC/PTC instruction emulator
// how many instructions to emulate (-1 is unlimited) before pausing next,
// so obviously we accept user commands until this becomes non-zero.
while (panelPause || runStepN <= 0)
{
int i, j, found = 0;
enum commandTokens commandToken;
assembly_t *assembly;
// Print breakpoint information.
if (breakpoint != NULL)
{
printf("Hit breakpoint #%ld at address %o-%02o-%o-%03o",
breakpoint - breakpoints, breakpoint->module, breakpoint->sector,
breakpoint->syllable, breakpoint->location);
switch (breakpoint->whichType)
{
case 0:
printf(" due to name match %s.\n", breakpoint->name);
break;
case 1:
printf(" due to source line-number match %d.\n",
breakpoint->number);
break;
case 2:
printf(" due to address match.\n");
break;
default:
printf(".\n");
}
if (breakpoint->temporary)
{
int i = breakpoint - breakpoints;
if (i < numBreakpoints - 1)
memmove(&breakpoints[i], &breakpoints[i + 1],
(numBreakpoints - i - 1) * sizeof(breakpoint_t));
numBreakpoints--;
printf("Temporary breakpoint deleted.\n");
}
}
nextCommand: ;
if (panelPause)
printf(
"\n*** CPU is paused by PTC panel ... no instructions will execute. ***\n");
// Display registers.
printf("\n HOP = ");
if (formHopDescription(state.hop, 0, 0, 0, hopBuffer, &hs))
{
printf("%s VAL = %-5s", hopBuffer, "n/a");
}
else
{
uint16_t instruction;
printf("%s VAL = ", hopBuffer);
if (fetchInstruction(hs.im, hs.is, hs.s, hs.loc, &instruction,
&instructionFromDataMemory))
{
printf("%-5s", "empty");
value = -1;
}
else
{
value = instruction;
printf("%05o", value);
}
}
// Search for the matching source line.
lineIndexInAssembly = findSourceLineByAddress(hs.im, hs.is, hs.s, hs.loc);
found = (lineIndexInAssembly != -1);
if (found)
assembly = assemblySourceLineByAddress;
printf(" ACC = %09o", state.acc);
if (ptc == 0)
printf(" P-Q = %09o", state.pq);
printf("\n");
formHopDescription(-1, 0, 017, 0377, hopBuffer, &hs2);
printf("(777)= %s", hopBuffer);
formHopDescription(-1, 0, 017, 0376, hopBuffer, &hs2);
printf(" (776)= %s\n", hopBuffer);
formHopDescription(state.hopSaver, 0, 0, 0, hopBuffer, &hs2);
if (ptc)
{
//printf(" TYP = %d ", state.busyCountTypewriter);
//printf(" CNT = %d ", typewriterCharsInLine);
printf(" PRT = %d ", state.busyCountPrinter);
printf(" CNT = %d ", state.prsParityDelayCount);
printf(" PR1 = %09o ", state.prsDelayedParity[1]);
printf(" PR2 = %09o ", state.prsDelayedParity[2]);
printf(" PR3 = %09o ", state.prsDelayedParity[3]);
printf(" CCB = %d ", state.busyCountCarriagePrinter);
printf(" CB = %09o ", state.cio210CarrBusy);
//printf(" 264 = %09o ", state.cio264Buffer);
printf(" INT = %09o", state.cio[0154]);
printf(" INH = %09o", state.interruptInhibitLatches);
printf(" INB = %d\n", state.masterInterruptLatch);
}
else
{
printf(" INT = %09o", state.pio[0137]);
printf(" INB = %d\n", state.masterInterruptLatch);
}
printf(" RET = %s\n", hopBuffer);
printf("Instructions: %lu", instructionCount);
printf(", Cycles: %lu", cycleCount);
printf(", Elapsed time: %f seconds\n",
cycleCount * SECONDS_PER_CYCLE * clockDivisor);
if (found)
{
printf("Source line: ");
if (numAssemblies > 1)
printf("%s:", assembly->name);
printf("%d\n", assembly->sourceLines[lineIndexInAssembly].lineNumber);
}
else
printf("Source line not found\n");
if (found)
{
sourceLine_t *sourceLine = &assembly->sourceLines[lineIndexInAssembly];
if (value != sourceLine->assembled)
{
printf("Code in memory (%05o) has changed from load (%05o).\n",
value, sourceLine->assembled);
printf("From source:\t%o-%02o-%o-%03o %o-%02o %05o %s\n",
sourceLine->module, sourceLine->sector, sourceLine->syllable,
sourceLine->loc, sourceLine->dm, sourceLine->ds,
sourceLine->assembled, sourceLine->line);
goto mustDisassemble;
}
else
printf("%o-%02o-%o-%03o %o-%02o %05o %s\n", sourceLine->module,
sourceLine->sector, sourceLine->syllable, sourceLine->loc,
sourceLine->dm, sourceLine->ds, sourceLine->assembled,
sourceLine->line);
}
else
{
printf("From source:\t(not found)\n");
mustDisassemble: ;
setupDisassembly(state.hop, NULL);
printf("Disassembled:\t%s\n", disassemble());
}
//nextCommand: ;
// Get a user command.
printf("\n> ");
fgets(lineBuffer, sizeof(lineBuffer), stdin);
printf("\n");
repeatLastCommand: ;
count = parseCommand();
if (count < 1)
{
if (lastLine[0])
{
strcpy(lineBuffer, lastLine);
goto repeatLastCommand;
}
continue;
}
commandToken = findCommandToken();
switch (commandToken)
{
case ctHELP:
lastLine[0] = 0;
for (i = j = 0; commandAssociations[i].token != ctNone; i++)
if (strlen(commandAssociations[i].syntax) > j)
j = strlen(commandAssociations[i].syntax);
printf("Available commands:\n");
for (i = 0; commandAssociations[i].token != ctNone; i++)
printf(" %-*s %s\n", j, commandAssociations[i].syntax,
commandAssociations[i].description);
printf("\nNotes:\n");
printf(" 1. Commands are not case sensitive.\n");
printf(" 2. Command abbreviations (like S for STEP) are accepted.\n");
printf(" 3. An empty command sometimes repeats previous command.\n");
printf(" 4. Addresses have the following formats:\n");
printf(" * Code memory: M-SS-Y-LLL, where M is memory module\n");
printf(" (0-7), SS is sector (00-17 octal), Y is syllable\n");
printf(" (0 or 1), and LLL is offset within the sector\n");
printf(" (000-377 octal).\n");
printf(" * Data memory: M-SS-LLL, where M is the memory module\n");
printf(" (0-7), SS is the sector (00-17 octal), and LLL is the\n");
printf(" offset within the sector (000-377 octal).\n");
printf(" * PIO port: PIO-LLL, where LLL is the port number\n");
printf(" (000-777 octal).\n");
printf(" * CIO port: CIO-LLL, where LLL is the port number\n");
printf(" (000-777 octal).\n");
printf(
" * PRS port: PRS-LLL, where LLL is the PRS operand number\n");
printf(" (000-777 octal).\n");
printf(" 5. Source line numbers (decimal) come from the input .src\n");
printf(" file, not from the original .lvdc source-code file.\n");
printf(" 6. A free-running emulation can be paused by hitting any\n");
printf(" key on the keyboard.\n");
printf(" 7. If optional assembly names ([asm:]) are omitted, then\n");
printf(" all loaded assemblies are searched, even though naming\n");
printf(" conflicts may occur. Only the first match encountered\n");
printf(" is affected by the command.\n");
printf(" 8. For commands like \"SET ... = n\", the number n is hex\n");
printf(" if it has leading 0x, octal if it merely has leading\n");
printf(" 0, and decimal otherwise.\n");
printf(" 9. All data displayed from memory or written to memory does\n");
printf(" NOT include parity bits or locations for parity bits.\n");
goto nextCommand;
case ctPANEL:
if (!ptc)
{
printf("Not emulating PTC; no PTC front panel available.\n");
goto nextCommand;
}
retVal = 0;
goto done;
case ctBACKTRACE:
{
int i, n = 20, numBacktraces = (firstEmptyBacktrace
- firstUsedBacktrace) % MAX_BACKTRACES;
printf("Recent HOP, TRA, TNZ, or TMI control transfers:\n");
if (count > 1)
n = atoi(fields[1]);
if (n > MAX_BACKTRACES - 1)
n = MAX_BACKTRACES - 1;
//printf("%d %d %d %d\n", firstEmptyBacktrace, firstUsedBacktrace, numBacktraces, n);
if (n > numBacktraces)
n = numBacktraces;
if (n == 0)
{
printf("\t(none)\n");
goto nextCommand;
}
i = (firstEmptyBacktrace - n) % MAX_BACKTRACES;
for (i = (firstEmptyBacktrace - n) % MAX_BACKTRACES;
i != firstEmptyBacktrace; i = NEXT_BACKTRACE(i))
{
backtrace_t *backtrace = &backtraces[i];
char buffer[32], *mnemonic;
hopStructure_t hs;
symbol_t *symbol;
switch (backtrace->fromInstruction & 017)
{
case 000:
mnemonic = "HOP";
break;
case 004:
mnemonic = "TNZ";
break;
case 010:
mnemonic = "TRA";
break;
case 014:
mnemonic = "TMI";
break;
default:
mnemonic = "TBD";
break;
}
formHopDescription(backtrace->fromWhere, 0, 0, 0, buffer, &hs);
symbol = findSymbolByAddress(hs.im, hs.is, hs.s, hs.loc);
printf(
"\tCycle count = %lu, Instruction count = %lu, from instruction %05o (%s) at %s",
backtrace->cycleCount, backtrace->instructionCount,
backtrace->fromInstruction, mnemonic, buffer);
if (symbol != NULL)
printf(" (%s)", symbol->name);
formHopDescription(backtrace->toWhere, 0, 0, 0, buffer, &hs);
symbol = findSymbolByAddress(hs.im, hs.is, hs.s, hs.loc);
printf(" to %s", buffer);
if (symbol != NULL)
printf(" (%s)", symbol->name);
printf(".\n");
}
}
goto nextCommand;
case ctRUN:
if (coldStart)
{
printf("Because the --cold-start command-line switch was used,\n");
printf("this will restore the emulation to a clean start state,\n");
printf("including all registers, memory, i/o ports, and so on.\n");
}
else
{
printf("Because the --cold-start command-line switch was not\n");
printf("used, this will restore the state of the emulation from\n");
printf("the most-recently created snapshot file (yaLVDC.core).\n");
printf("If your desire is instead to have a completely clean\n");
printf("startup, exit yaLVDC and rerun it with --cold-start.\n");
printf("Or, manually delete the core file before proceeding.\n");
}
printf("Are you sure? Input Y to proceed, anything else to cancel: ");
fgets(lineBuffer, sizeof(lineBuffer), stdin);
printf("\n");
if (toupper(lineBuffer[0]) == 'Y')
{
state.restart = 1;
printf("Restoring state, restarting emulation.\n");
retVal = 0;
goto done;
}
else
printf("*** Canceled ***\n");
goto nextCommand;
case ctX:
if (count == 2 || (count >= 3 && fields[1][0] == '/'))
{
int module, sector, syllable, location, printCount = 1;
char formatType = 'o', ss[256], *adrField;
enum addressType_t at;
if (count >= 3 && fields[1][0] == '/')
{
adrField = fields[2];
if (2
!= sscanf(fields[1], "/%d%c%s", &printCount, &formatType,
ss))
{
formatType = 'o';
if (1 != sscanf(fields[1], "/%d%s", &printCount, ss))
{
printCount = 1;
if (1 != sscanf(fields[1], "/%c%s", &formatType, ss))
{
printf("Unrecognized format %s\n", fields[1]);
break;
}
}
}
if (formatType != 'b' && formatType != 'd' && formatType != 'o')
{
printf("Unrecognized format %s\n", fields[1]);
break;
}
}
else
adrField = fields[1];
if (adrField[0] == '&')
{
symbol_t *symbol;
symbol = findSymbol(adrField + 1, 1);
if (symbol != NULL)
{
at = atCode;
module = symbol->module;
sector = symbol->sector;
syllable = symbol->syllable;
location = symbol->loc;
}
else
{
symbol = findSymbol(adrField + 1, 0);
if (symbol != NULL)
{
at = atData;
module = symbol->module;
sector = symbol->sector;
syllable = 2;
location = symbol->loc;
}
}
}
else
at = parseInputAddress(adrField, &module, §or, &syllable,
&location);
for (; printCount > 0; printCount--)
{
switch (at)
{
case atCode:
if (module < 0 || module > 7 || sector < 0 || sector > 017
|| syllable < 0 || syllable > 1 || location < 0
|| location > 0377)
goto badAddressX;
formatFetchedValue(
state.core[module][sector][syllable][location],
formatType, 0, syllable);
printf("%o-%02o-%o-%03o: %s\n", module, sector, syllable,
location, formattedFetchedValue);
location++;
break;
case atData:
if (module < 0 || module > 7 || sector < 0 || sector > 017
|| location < 0 || location > 0377)
goto badAddressX;
formatFetchedValue(state.core[module][sector][2][location],
formatType, 1, 2);
printf("%o-%02o-%03o: %s\n", module, sector, location,
formattedFetchedValue);
location++;
break;
case atPio:
if (location < 0 || location > 0777)
goto badAddressX;
formatFetchedValue(state.pio[location], formatType, 1, 2);
printf("PIO-%03o: %s\n", location, formattedFetchedValue);
location++;
break;
case atCio:
if (location < 0 || location > 0777)
goto badAddressX;
formatFetchedValue(state.cio[location], formatType, 1, 2);
printf("CIO-%03o: %s\n", location, formattedFetchedValue);
location++;
break;
case atPrs:
if (location < 0 || location > 0777)
goto badAddressX;
formatFetchedValue(state.prs[location], formatType, 1, 2);
printf("PRS-%03o: %s\n", location, formattedFetchedValue);
break;
default:
printf("Unrecognized format\n");
break;
}
}
}
else
printf("Unrecognized command\n");
if (0)
{
badAddressX: ;
printf("Nonexistent address.\n");
}
goto nextCommand;
case ctSET:
{
int assignment = 0;
if (count > 1)
{
if (!strcasecmp(fields[1], "variable") || fields[1][0] == '*')
assignment = 1;
else if (isalpha(fields[1][0])
&& (strstr(fields[1], "=") != NULL
|| (count > 2 && strstr(fields[2], "=") != NULL)))
assignment = 1;
}
if (count >= 3 && !strcasecmp(fields[1], "listsize"))
listSize = atoi(fields[2]);
else if (assignment)
{
// At this point, we know that the command can't be anything _other_
// than an assignment of a value to a variable or a memory location,
// but we don't know that is actually valid wither in format or data.
int start = 1, intValue, overflow, conflict = 0;
char *equal, *destination, *value, *format, dummy[256];
int module, sector, syllable, location;
int32_t *destinationPointer;
enum addressType_t at;
if (!strcasecmp(fields[start], "variable")) // fields[1] literally "variable".
start++; // ... so the assignment begins at fields[2].
if (start >= count)
goto badSetCommand;
// Because we don't know whether/how the '=' is surrounded by spaces,
// nor even that the '=' is present, the value and the destination for
// the assignment may be spread out over fields[start], fields[start+1],
// and fields[start+2]. Consequently, we to do some parsing to find
// the fields we need and verify the format.
destination = fields[start];
equal = strstr(fields[start], "=");
if (equal != NULL) // fields[start] includes the "=".
{
*equal = 0;
if (equal[1] != 0)
value = equal + 1;
else
value = fields[start + 1];
}
else if (fields[start + 1][0] != '=')
goto badSetCommand;
else
{
if (fields[start + 1][1] != 0)
value = &fields[start + 1][1];
else if (start + 2 >= count)
goto badSetCommand;
else
value = fields[start + 2];
}
// The strings named "destination" and "value" are now set.
// Parse the value to determine its format, and convert it
// to an integer.
if (!strncasecmp(value, "0x", 2))
{
value += 2;
format = "%x%s";
}
else if (value[0] == '0')
format = "%o%s";
else
format = "%d%s";
if (1 != sscanf(value, format, &intValue, dummy))
goto badSetCommand;
// Determine the address at which to store the value.
at = atNone;
if (destination[0] == '*')
{
at = parseInputAddress(destination + 1, &module, §or,
&syllable, &location);
}
else
{
symbol_t *symbol;
symbol = findSymbol(destination, 0);
if (symbol != NULL)
{
at = atData;
module = symbol->module;
sector = symbol->sector;
syllable = 2;
location = symbol->loc;
}
else
{
symbol = findSymbol(destination, 1);
if (symbol != NULL)
{
at = atCode;
module = symbol->module;
sector = symbol->sector;
syllable = symbol->syllable;
location = symbol->loc;
}
}
}
if (at == atNone)
goto badSetCommand;
// Range checks.
if (at == atCode || at == atData)
if ((ptc && module > 1) || module < 0 || module > 7
|| sector < 0 || sector > 017 || syllable < 0
|| syllable > 2 || location < 0 || location > 0377)
{
printf("Code or data memory address out of range.\n");
goto badSetCommand;
}
if (at == atPio && (location < 0 || location > 0777))
{
printf("PIO port out of range.\n");
goto badSetCommand;
}
if (at == atCio && (location < 0 || location > 0777))
{
printf("CIO port out of range.\n");
goto badSetCommand;
}
if (at == atCode)
{
overflow = intValue & ~017777;
if (overflow != 0)
{
printf("Value out of range.\n");
goto badSetCommand;
}
}
else
{
overflow = intValue & ~0377777777;
if (overflow != 0 && overflow != ~0377777777)
{
printf("Value out of range.\n");
goto badSetCommand;
}
intValue &= 0377777777;
}
// Sanity clause.
conflict = 0;
switch (at)
{
case atCode:
printf(
"Note that the destination address is in code memory.\n");
destinationPointer =
&state.core[module][sector][syllable][location];
if (state.core[module][sector][2][location] != -1)
{
conflict = 1;
printf(
"Note that this will conflict with data memory contents.\n");
}
break;
case atData:
destinationPointer = &state.core[module][sector][2][location];
if (state.core[module][sector][0][location] != -1
|| state.core[module][sector][1][location] != -1)
{
conflict = 1;
printf(
"Note that this will conflict with code memory contents.\n");
}
break;
case atPio:
destinationPointer = &state.pio[location];
state.pioChange = location;
break;
case atCio:
destinationPointer = &state.cio[location];
state.cioChange = location;
break;
case atPrs:
destinationPointer = &state.prs[location];
state.prsChange = location;
default:
break;
}
if (conflict || at == atCode)
{
printf(
"Are you sure? Input Y to continue, anything else to cancel: ");
fgets(lineBuffer, sizeof(lineBuffer), stdin);
printf("\n");
if (toupper(lineBuffer[0]) != 'Y')
{
printf("*** Canceled ***\n");
goto nextCommand;
}
}
// Perform the assignment.
if (*destinationPointer == -1)
printf("Overwriting empty location with %09o.\n", intValue);
else
printf("Overwriting value %09o with %09o.\n",
*destinationPointer, intValue);
*destinationPointer = intValue;
if (at == atPio || at == atCio || at == atPrs)
processInterruptsAndIO();
}
else
printf("Unrecognized SET command.\n");
}
if (0)
{
badSetCommand: ;
printf("Illegal SET command.\n");
}
goto nextCommand;
case ctSHOW:
if (count >= 2 && !strcasecmp(fields[1], "listsize"))
printf("Default block size for LIST is %d lines.\n", listSize);
goto nextCommand;
case ctDISASSEMBLE:
if (count >= 3 || count < 2)
{
int im = hs.im, is = hs.is, s = hs.s, startloc = hs.loc, endloc,
dm = hs.dm, ds = hs.ds, warn = 1;
if (count == 3 && strcmp(fields[1], "-")
&& 2 == sscanf(fields[2], "%o-%o", &dm, &ds) && dm <= 7
&& ds <= 017)
{
endloc = startloc - 1;
startloc -= listSize;
if (startloc < 0)
startloc = 0;
if (endloc < 0)
endloc = 0;
}
else if (count == 1)
{
warn = 0;
endloc = startloc + listSize - 1;
if (endloc > 0377)
endloc = 0377;
}
else if (count >= 4
&& 4
== sscanf(fields[1], "%o-%o-%o-%o", &im, &is, &s, &startloc)
&& 1 == sscanf(fields[2], "%o", &endloc)
&& 2 == sscanf(fields[3], "%o-%o", &dm, &ds) && im <= 7
&& is <= 017 && s <= 1 && startloc <= 0377 && endloc <= 0377
&& dm <= 7 && ds <= 017)
{
}
else
{
printf("Invalid arguments.\n");
goto nextCommand;
}
if (warn)
printf("Disassembling (note: starting DM/DS not known):\n");
else
printf("Disassembling:\n");
if (endloc < startloc)
printf("\t(empty address range)\n");
else
{
hopStructure_t dhs;
dhs.im = im;
dhs.is = is;
dhs.s = s;
dhs.loc = startloc;
dhs.dm = dm;
dhs.ds = ds;
if (setupDisassembly(-1, &dhs))
printf("Unable to set up disassembly.\n");
else
for (; startloc <= endloc; startloc++)
{
printf("%10s%s\n", "", disassemble());
}
}
}
goto nextCommand;
case ctLIST:
{
char *colon = NULL;
if (count > 1)
colon = strstr(fields[1], ":");
if (count == 1 && found)
printSourceBlock(assembly, lineIndexInAssembly,
lineIndexInAssembly + listSize);
else if (count < 2)
{
}
else if (!strcmp(fields[1], "-") && found)
printSourceBlock(assembly, lineIndexInAssembly - listSize,
lineIndexInAssembly);
else if ((colon == NULL && isdigit(fields[1][0]))
|| (colon != NULL && isdigit(colon[1])))
{
sourceLine_t *sourceLine;
sourceLine = findLineNumber(fields[1]);
if (sourceLine == NULL)
printf("Line number not found.\n");
else
printSourceBlock(lineNumberAssembly,
sourceLine - lineNumberAssembly->sourceLines,
sourceLine - lineNumberAssembly->sourceLines + listSize);
}
else if ((colon == NULL && isalpha(fields[1][0]))
|| (colon != NULL && isalpha(colon[1])))
{
symbol_t *symbol;
symbol = findSymbol(fields[1], 1);
if (symbol == NULL)
printf("Symbol not found.\n");
else
{
int index = findSourceLineByAddress(symbol->module,
symbol->sector, symbol->syllable, symbol->loc);
printSourceBlock(assemblySourceLineByAddress, index,
index + listSize);
}
}
else
printf("Unrecognized command.\n");
}
goto nextCommand;
case ctQUIT:
lastLine[0] = 0;
goto done;
case ctNEXT:
case ctSTEP:
inNextNotStep = (commandToken == ctNEXT);
inNextHop = 0;
if (count == 1)
runStepN = 1;
else
runStepN = atoi(fields[1]);
strcpy(lastLine, lineBuffer);
break;
case ctCONTINUE:
runStepN = INT_MAX;
strcpy(lastLine, lineBuffer);
break;
case ctINFO:
if (count >= 2)
{
int i;
breakpoint_t *breakpoint;
assembly_t *assembly;
if (!strcasecmp(fields[1], "assemblies"))
{
if (numAssemblies <= 0)
printf("No assemblies are loaded.\n");
else
for (i = 0, assembly = assemblies; i < numAssemblies;
i++, assembly++)
printf(
"Assembly %s, %d symbols, %d source lines, %d code words, %d data words.\n",
assembly->name, assembly->numSymbols,
assembly->numSourceLines, assembly->codeWords,
assembly->dataWords);
}
else if (!strcasecmp(fields[1], "breakpoints"))
{
if (numBreakpoints <= 0)
printf("No breakpoints are set.\n");
for (i = 0, breakpoint = breakpoints; i < numBreakpoints;
i++, breakpoint++)
{
printf("Breakpoint (%s) at %o-%02o-%o-%03o",
breakpoint->temporary ? "temporary" : "permanent",
breakpoint->module, breakpoint->sector,
breakpoint->syllable, breakpoint->location);
switch (breakpoint->whichType)
{
case 0:
printf(", name match %s.\n", breakpoint->name);
break;
case 1:
printf(", source line-number match %d.\n",
breakpoint->number);
break;
case 2:
printf(", address match.\n");
break;
default:
printf(".\n");
}
}
}
else if (!strcasecmp(fields[1], "break"))
{
if (numBreakpoints <= 0)
printf("No breakpoints are set.\n");
for (i = 0, breakpoint = breakpoints; i < numBreakpoints;
i++, breakpoint++)
{
printf("%d: %o-%02o-%o-%03o (%s)\n", i, breakpoint->module,
breakpoint->sector, breakpoint->syllable,
breakpoint->location,
breakpoint->temporary ? "temporary" : "permanent");
}
}
else
printf("Unrecognized INFO option.\n");
}
goto nextCommand;
case ctBREAK:
case ctCLEAR:
case ctTBREAK:
if (count >= 2)
{
int i, is, im, s, loc, ok = 0, whichType;
char *colon;
breakpoint_t *breakpoint;
colon = strstr(fields[1], ":");
if (colon == NULL && fields[1][0] == '*')
{
whichType = 2;
if (4
!= sscanf(&fields[1][1], "%o-%o-%o-%o", &im, &is, &s, &loc)
|| (s != 1 && s != 0))
printf("Not a location in code memory.\n");
else
ok = 1;
}
else if ((colon == NULL && isdigit(fields[1][0]))
|| (colon != NULL && isdigit(colon[1])))
{
sourceLine_t *sourceLine;
whichType = 1;
sourceLine = findLineNumber(fields[1]);
if (sourceLine != NULL)
{
im = sourceLine->module;
is = sourceLine->sector;
s = sourceLine->syllable;
loc = sourceLine->loc;
ok = 1;
}
else
printf("Line number not found.\n");
}
else if ((colon == NULL && isalpha(fields[1][0]))
|| (colon != NULL && isalpha(colon[1])))
{
symbol_t *symbol;
whichType = 0;
symbol = findSymbol(fields[1], 1);
if (symbol != NULL)
{
im = symbol->module;
is = symbol->sector;
s = symbol->syllable;
loc = symbol->loc;
ok = 1;
}
else
printf("Symbol not found.\n");
}
else
printf("Breakpoint type not recognized.\n");
// If an address has been determined at which the breakpoint is
// desired ...
if (ok)
{
// Find existing breakpoint, if any.
for (i = 0, breakpoint = breakpoints; i < numBreakpoints;
i++, breakpoint++)
if (im == breakpoint->module && is == breakpoint->sector
&& s == breakpoint->syllable
&& loc == breakpoint->location)
{
if (commandToken == ctCLEAR)
{
if (i < numBreakpoints - 1)
memmove(&breakpoints[i], &breakpoints[i + 1],
(numBreakpoints - i - 1)
* sizeof(breakpoint_t));
numBreakpoints--;
printf("Breakpoint #%d deleted.\n", i);
}
else if (breakpoint->temporary && commandToken == ctBREAK)
{
breakpoint->temporary = 0;
printf(
"Existing breakpoint changed from temporary to permanent\n");
}
else if (!breakpoint->temporary
&& commandToken == ctTBREAK)
{
breakpoint->temporary = 1;
printf(
"Existing breakpoint changed from permanent to temporary\n");
}
else
printf("Breakpoint already exists.\n");
ok = 0;
break;
}
}
if (ok && commandToken != ctCLEAR)
{
// If the breakpoint doesn't already exist ...
if (i >= numBreakpoints)
{
if (numBreakpoints >= MAX_BREAKPOINTS)
printf("The breakpoint table is full already.\n");
else
{
breakpoints[i].module = im;
breakpoints[i].sector = is;
breakpoints[i].syllable = s;
breakpoints[i].location = loc;
breakpoints[i].whichType = whichType;
breakpoints[i].temporary = (commandToken == ctTBREAK);
if (whichType == 1)
breakpoints[i].number = atoi(fields[1]);
if (whichType == 0)
strcpy(breakpoints[i].name, fields[1]);
numBreakpoints++;
}
}
}
}
goto nextCommand;
case ctJUMP:
case ctGOTO:
{
int hop, dm, ds;
hopStructure_t hs;
char c;
symbol_t *symbol = NULL;
if (count == 2)
{
if (1 == sscanf(fields[1], "%o%c", &hop, &c) && hop >= 0
&& hop <= 0400000000)
goto foundJump;
else if (isalpha(fields[1][0]))
{
symbol = findSymbol(fields[1], 0);
if (symbol == NULL)
{
printf("Symbol not found in data memory.\n");
goto nextCommand;
}
if (fetchData(symbol->module, 0, symbol->sector, symbol->loc, &hop, &dataFromInstructionMemory))
{
printf("Cannot fetch value of HOP constant from memory.\n");
goto nextCommand;
}
goto foundJump;
}
}
else if (count == 3 && 2 == sscanf(fields[2], "%o-%o%c", &dm, &ds, &c))
{
if (fields[1][0] == '*')
{
int module, sector, syllable, location;
if (4 == sscanf(fields[1], "*%o-%o-%o-%o%c", &module, §or, &syllable, &location, &c))
{
hs.im = module;
hs.is = sector;
hs.s = syllable;
hs.loc = location;
goto hopDescriptionFound;
}
}
else if (isalpha(fields[1][0]))
{
symbol = findSymbol(fields[1], 1);
if (symbol == NULL)
{
printf("Symbol not found in code memory.\n");
goto nextCommand;
}
hs.im = symbol->module;
hs.is = symbol->sector;
hs.s = symbol->syllable;
hs.loc = symbol->loc;
hopDescriptionFound:;
hs.dm = dm;
hs.ds = ds;
hs.dupdn = 0;
hs.dupin = 0;
if (formHopConstant(&hs, &hop))
{
printf("Cannot form HOP constant.\n");
goto nextCommand;
}
goto foundJump;
}
}
printf("Illegal syntax.\n");
goto nextCommand;
foundJump: ;
state.hop = hop;
if (commandToken == ctJUMP)
{
runStepN = INT_MAX;
break;
}
else
goto nextCommand;
}
case ctDELETE:
if (count < 2)
{
numBreakpoints = 0;
printf("All breakpoints deleted.\n");
}
else
{
i = atoi(fields[1]);
if (i >= 0 && i < numBreakpoints)
{
if (i < numBreakpoints - 1)
memmove(&breakpoints[i], &breakpoints[i + 1],
(numBreakpoints - i - 1) * sizeof(breakpoint_t));
numBreakpoints--;
printf("Breakpoint #%d deleted.\n", i);
}
}
goto nextCommand;
default:
lastLine[0] = 0;
printf("Unknown command: %s\n", fields[0]);
goto nextCommand;
}
}
retVal = 0;
done: ;
return (retVal);
}