Parse2DEC.c
/*
Copyright 2003-2004,2009 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: Parse2DEC.c
Purpose: Assembles the 2DEC pseudo-op.
Mod History: 04/15/03 RSB Began.
07/23/04 RSB Added VN.
09/04/04 RSB DEC and 2DEC weren't detecting -0 -- i.e.,
-0 was converted to +0.
02/28/09 RSB Several incorrect string-compares
(of the form string=="" rather than
string[0]==0) which had for some reason
previously been working were fixed.
2012-09-26 JL Add support for a variety of number formats as
seen in Colossus237 sources, e.g.
- E and B fields appended to operand.
- "B - 1" types where characters are spread
between Operand, Mod1, Mod2.
There are still a number of pathological
cases left.
2012-10-09 JL Handle the case (e.g. 2DEC*) where the number
spills over into the Extra field. Fix handling
of numbers where the exponents are all in the
operand field.
*/
#include "yaYUL.h"
#include <stdlib.h>
#include <math.h>
#include <string.h>
//-------------------------------------------------------------------------
// Converts a string like "E+-n" or "B+-n" to a scale factor.
double ScaleFactor(char *s)
{
int n;
if (*s == 0)
return (1.0);
if (*s == 'E') {
n = atoi(s + 1);
return (pow(10.0, n));
}
if (*s == 'B') {
n = atoi(s + 1);
return (pow(2.0, n));
}
return (1.0);
}
//-------------------------------------------------------------------------
// Returns non-zero on unrecoverable error We don't do a heckuva lot of
// error-checking in this version.
int Parse2DECstar(ParseInput_t *InRecord, ParseOutput_t *OutRecord)
{
return (Parse2DEC(InRecord, OutRecord));
}
int Parse2DEC(ParseInput_t *InRecord, ParseOutput_t *OutRecord)
{
double x;
double tmpval;
int Sign, Value, i;
char tmpoperand[32];
char tmpmod1[32];
char tmpmod2[32];
char *ptmpmod1 = NULL, *ptmpmod2 = NULL;
//#ifdef YAYUL_TRACE
// printf("\n");
// printf("--- 2DEC: (original) operand=\"%s\" mod1=\"%s\" mod2=\"%s\" extra=\"%s\"\n", InRecord->Operand, InRecord->Mod1, InRecord->Mod2, InRecord->Extra);
//#endif
IncPc(&InRecord->ProgramCounter, 2, &OutRecord->ProgramCounter);
if (!OutRecord->ProgramCounter.Invalid && OutRecord->ProgramCounter.Overflow) {
strcpy(OutRecord->ErrorMessage, "Next code may overflow storage.");
OutRecord->Warning = 1;
}
OutRecord->EBank = InRecord->EBank;
OutRecord->SBank = InRecord->SBank;
OutRecord->Words[0] = ILLEGAL_SYMBOL_VALUE;
OutRecord->Words[1] = ILLEGAL_SYMBOL_VALUE;
OutRecord->NumWords = 2;
if (InRecord->Extend && !InRecord->IndexValid) {
strcpy(OutRecord->ErrorMessage, "Illegally preceded by EXTEND.");
OutRecord->Fatal = 1;
OutRecord->Extend = 0;
}
if (InRecord->IndexValid) {
strcpy(OutRecord->ErrorMessage, "Illegally preceded by INDEX.");
OutRecord->Fatal = 1;
OutRecord->IndexValid = 0;
}
if (InRecord->Operand[0] == 0) {
strcpy(OutRecord->ErrorMessage, "Operand is missing.");
OutRecord->Fatal = 1;
return (0);
}
// Handle numbers where only scale factor(s) are supplied.
if (*InRecord->Operand == 'E' || *InRecord->Operand == 'B') {
tmpval = 1.0;
ptmpmod1 = InRecord->Operand;
ptmpmod2 = InRecord->Mod1;
} else {
char *estr = strchr(InRecord->Operand, 'E');
char *bstr = strchr(InRecord->Operand, 'B');
memset(tmpoperand, 0, 32);
memset(tmpmod1, 0, 32);
memset(tmpmod2, 0, 32);
// Handle the case where the whole string is in Operand. Split out the modifiers.
if (estr || bstr) {
if (estr && bstr) {
if (estr < bstr) {
// NNNNENN[BNN]
strcpy(tmpmod2, bstr);
*bstr = '\0';
strcpy(tmpmod1, estr);
*estr = '\0';
} else {
// NNNNBNN[ENN] - does this ever occur? Catch it anyway, just in case.
strcpy(tmpmod1, estr);
*estr = '\0';
strcpy(tmpmod2, bstr);
*bstr = '\0';
}
} else {
if (estr) {
strcpy(tmpmod1, estr);
*estr = '\0';
} else {
strcpy(tmpmod1, bstr);
*bstr = '\0';
}
}
strcpy(tmpoperand, InRecord->Operand);
tmpval = strtod(tmpoperand, NULL);
ptmpmod1 = tmpmod1;
ptmpmod2 = tmpmod2;
} else {
// Handle the case where the Enn or Bnn is split between Mod1, Mod2, and Extra.
if (InRecord->Extra) {
if (strcmp(InRecord->Mod1, "E") == 0 || strcmp(InRecord->Mod1, "B") == 0) {
strcpy(tmpmod1, InRecord->Mod1);
strcat(tmpmod1, InRecord->Mod2);
ptmpmod1 = tmpmod1;
ptmpmod2 = InRecord->Extra;
} else {
strcpy(tmpmod2, InRecord->Mod2);
strcat(tmpmod2, InRecord->Extra);
ptmpmod1 = InRecord->Mod1;
ptmpmod2 = tmpmod2;
}
} else {
if (strcmp(InRecord->Mod1, "E") == 0 || strcmp(InRecord->Mod1, "B") == 0) {
strcpy(tmpmod1, InRecord->Mod1);
strcat(tmpmod1, InRecord->Mod2);
ptmpmod1 = tmpmod1;
ptmpmod2 = tmpmod2;
} else {
ptmpmod1 = InRecord->Mod1;
ptmpmod2 = InRecord->Mod2;
}
}
tmpval = strtod(InRecord->Operand, NULL);
}
}
//#ifdef YAYUL_TRACE
// printf("--- 2DEC: (modified) operand=\"%f\" mod1=\"%s\" mod2=\"%s\"\n", tmpval, ptmpmod1, ptmpmod2);
//#endif
// Under some circumstances, add a default scale factor.
if (strstr(InRecord->Operand, ".") == NULL && *InRecord->Mod1 == 0 && *InRecord->Mod2 == 0)
InRecord->Mod1 = "B-28";
// Compute the constant as a floating-point number.
x = tmpval * ScaleFactor(ptmpmod1) * ScaleFactor(ptmpmod2);
// Convert to 1's complement format.
Sign = 0;
if (InRecord->Operand[0] == '-') {
// x < 0
Sign = 1;
x = -x;
}
if (fmod(x, 1.0) == 0.0) {
// Integer: just convert directly to octal.
Value = (int)x;
} else {
// Floating point: scale. FP numbers > 1.0 are an error.
if (x >= 1.0)
return (1);
for (Value = 0, i = 0; i < 28; i++) {
Value = Value << 1;
if (x >= 0.5) {
Value++;
x -= 0.5;
}
x *= 2;
}
if (x >= 0.5 && Value < 0x0fffffff)
Value++;
}
i = Value & 0x00003fff;
Value = (Value >> 14) & 0x00003fff;
if (Sign) {
Value = ~Value;
i = ~i;
i &= 0x00007fff;
Value &= 0x00007fff;
}
OutRecord->Words[0] = Value;
OutRecord->Words[1] = i;
return (0);
}
//-------------------------------------------------------------------------
// Returns non-zero on unrecoverable error We don't do a heckuva lot of
// error-checking in this version.
int ParseDECstar(ParseInput_t *InRecord, ParseOutput_t *OutRecord)
{
return (ParseDEC(InRecord, OutRecord));
}
int ParseDEC(ParseInput_t *InRecord, ParseOutput_t *OutRecord)
{
double x;
double tmpval;
char *tmpmod1 = NULL, *tmpmod2 = NULL;
int Sign, Value, i;
IncPc(&InRecord->ProgramCounter, 1, &OutRecord->ProgramCounter);
if (!OutRecord->ProgramCounter.Invalid && OutRecord->ProgramCounter.Overflow) {
strcpy(OutRecord->ErrorMessage, "Next code may overflow storage.");
OutRecord->Warning = 1;
}
OutRecord->EBank = InRecord->EBank;
OutRecord->SBank = InRecord->SBank;
OutRecord->Words[0] = ILLEGAL_SYMBOL_VALUE;
OutRecord->NumWords = 1;
if (InRecord->Extend && !InRecord->IndexValid) {
strcpy(OutRecord->ErrorMessage, "Illegally preceded by EXTEND.");
OutRecord->Fatal = 1;
OutRecord->Extend = 0;
}
if (InRecord->IndexValid) {
strcpy(OutRecord->ErrorMessage, "Illegally preceded by INDEX.");
OutRecord->Fatal = 1;
OutRecord->IndexValid = 0;
}
if (InRecord->Operand[0] == 0) {
strcpy(OutRecord->ErrorMessage, "Operand is missing.");
OutRecord->Fatal = 1;
return (0);
}
// Handle numbers where only scale factor(s) are supplied.
if (*InRecord->Operand == 'E' || *InRecord->Operand == 'B') {
tmpval = 1.0;
tmpmod1 = InRecord->Operand;
tmpmod2 = InRecord->Mod1;
} else {
tmpval = strtod(InRecord->Operand, NULL);
tmpmod1 = InRecord->Mod1;
tmpmod2 = InRecord->Mod2;
}
// Under some circumstances, add a default scale factor.
if (strstr(InRecord->Operand, ".") == NULL && *InRecord->Mod1 == 0 && *InRecord->Mod2 == 0)
InRecord->Mod1 = "B-14";
// Compute the constant as a floating-point number.
x = tmpval * ScaleFactor(tmpmod1) * ScaleFactor(tmpmod2);
// Convert to 1's complement format.
Sign = 0;
if (InRecord->Operand[0] == '-') {
// x < 0
Sign = 1;
x = -x;
}
if (fmod(x, 1.0) == 0.0) {
// Integer: just convert directly to octal.
Value = (int)x;
} else {
// Floating point: scale. FP numbers > 1.0 are an error.
if (x >= 1.0)
return (1);
for (Value = 0, i = 0; i < 14; i++) {
Value = Value << 1;
if (x >= 0.5) {
Value++;
x -= 0.5;
}
x *= 2;
}
if (x >= 0.5 && Value < 0x03fff)
Value++;
}
if (Sign)
Value = 0x7FFF & ~Value;
OutRecord->Words[0] = Value;
return (0);
}
//----------------------------------------------------------------------------
// VN is a slightly-changed knockoff of DEC, designed to pack verb/noun
// specs into a single word of memory.
int ParseVN(ParseInput_t *InRecord, ParseOutput_t *OutRecord)
{
char c;
unsigned Value;
IncPc(&InRecord->ProgramCounter, 1, &OutRecord->ProgramCounter);
if (!OutRecord->ProgramCounter.Invalid && OutRecord->ProgramCounter.Overflow) {
strcpy(OutRecord->ErrorMessage, "Next code may overflow storage.");
OutRecord->Warning = 1;
}
OutRecord->EBank = InRecord->EBank;
OutRecord->SBank = InRecord->SBank;
OutRecord->Words[0] = ILLEGAL_SYMBOL_VALUE;
OutRecord->NumWords = 1;
if (InRecord->Extend && !InRecord->IndexValid) {
strcpy(OutRecord->ErrorMessage, "Illegally preceded by EXTEND.");
OutRecord->Fatal = 1;
OutRecord->Extend = 0;
}
if (InRecord->IndexValid) {
strcpy(OutRecord->ErrorMessage, "Illegally preceded by INDEX.");
OutRecord->Fatal = 1;
OutRecord->IndexValid = 0;
}
if (InRecord->Operand[0] == 0) {
strcpy(OutRecord->ErrorMessage, "Operand is missing.");
OutRecord->Fatal = 1;
return (0);
}
// The idea here is that the operand is a decimal number, of which the
// lower two digits are simply placed in the output, whereas the upper two
// digits are multiplied by 128 before been added to the output.
// (Isn't the sscanf clever? The final %c checks for garbage following
// the decimal number.)
if (sscanf(InRecord->Operand,"%u%c", &Value, &c) != 1) {
strcpy(OutRecord->ErrorMessage, "Operand is not a decimal number.");
OutRecord->Fatal = 1;
return (0);
}
Value = (Value % 100) | ((Value / 100) << 7);
OutRecord->Words[0] = Value;
return (0);
}