Files
open-roms/tools/generate_constants.cc
T

297 lines
6.8 KiB
C++

//
// Utility to generate various floating points constants
// in a Commodore-specific variable format
//
#include "common.h"
#include <unistd.h>
#include <cmath>
#include <cstdio>
#include <fstream>
#include <iomanip>
#include <vector>
//
// Basic types definition
//
typedef struct ConstEntry
{
ConstEntry(const std::string &constName, double constValue) :
constName(constName),
constValue(constValue),
outString(".error \"not generated\"")
{
};
const std::string constName;
const double constValue;
std::string outString;
} ConstEntry;
//
// Constants to output
//
std::vector<ConstEntry> GLOBAL_constants =
{
// Constants from:
// - https://www.c64-wiki.com/wiki/BASIC-ROM
// Computes Mapping the Commodore 64, pages 103, 105, 113, 114, 116
ConstEntry( "QUARTER", 0.25 ),
ConstEntry( "HALF", 0.5 ),
ConstEntry( "NEG_HALF", -0.5 ),
ConstEntry( "ONE", 1.0 ),
ConstEntry( "TEN", 10.0 ),
ConstEntry( "NEG_32768", -32768.0 ),
ConstEntry( "HALF_PI", M_PI / 2.0 ),
ConstEntry( "PI", M_PI ),
ConstEntry( "DOUBLE_PI", M_PI * 2.0 ),
ConstEntry( "SQR_2", std::sqrt(2.0) ),
ConstEntry( "INV_SQR_2", 1.0 / std::sqrt(2.0) ),
ConstEntry( "LOG_2", std::log(2.0) ),
ConstEntry( "INV_LOG_2", 1.0 / std::log(2.0) ),
// Constants for exp approximation, taken from https://www.c64-wiki.com/wiki/POLY1
// original source is probably "Computer Approximations" by John Fraser Hart et al. (ISBN 0-88275-642-7)
ConstEntry( "POLY_EXP_1", 2.1498763701e-5 ),
ConstEntry( "POLY_EXP_2", 1.4352314037e-4 ),
ConstEntry( "POLY_EXP_3", 1.3422634825e-3 ),
ConstEntry( "POLY_EXP_4", 9.6140170135e-3 ),
ConstEntry( "POLY_EXP_5", 5.5505126860e-2 ),
ConstEntry( "POLY_EXP_6", 0.24022638460 ),
ConstEntry( "POLY_EXP_7", 0.69314718618 ),
ConstEntry( "POLY_EXP_8", 1.0 ),
// Constants for sin approximation, source as for exp above
ConstEntry( "POLY_SIN_1", -14.381390672 ),
ConstEntry( "POLY_SIN_2", 42.007797122 ),
ConstEntry( "POLY_SIN_3", -76.704170257 ),
ConstEntry( "POLY_SIN_4", 81.605223686 ),
ConstEntry( "POLY_SIN_5", -41.341702104 ),
ConstEntry( "POLY_SIN_6", 6.2831853069 ),
// Constants for log approximation, source as for exp above
ConstEntry( "POLY_LOG_1", 0.43425594189 ),
ConstEntry( "POLY_LOG_2", 0.57658454124 ),
ConstEntry( "POLY_LOG_3", 0.96180075919 ),
ConstEntry( "POLY_LOG_4", 2.8853900731 ),
// Constants for atn approximation, source as for exp above
ConstEntry( "POLY_ATN_1", -6.8479391189e-4 ),
ConstEntry( "POLY_ATN_2", 4.8509421558e-3 ),
ConstEntry( "POLY_ATN_3", -1.6111701843e-2 ),
ConstEntry( "POLY_ATN_4", 3.4209638048e-2 ),
ConstEntry( "POLY_ATN_5", -5.4279132761e-2 ),
ConstEntry( "POLY_ATN_6", 7.2457196540e-2 ),
ConstEntry( "POLY_ATN_7", -8.9802395378e-2 ),
ConstEntry( "POLY_ATN_8", 0.11093241343 ),
ConstEntry( "POLY_ATN_9", -0.14283980767 ),
ConstEntry( "POLY_ATN_10", 0.19999912049 ),
ConstEntry( "POLY_ATN_11", -0.33333331568 ),
ConstEntry( "POLY_ATN_12", 1.0 ),
};
//
// Command line settings
//
std::string CMD_outFile = "out.s";
//
// Common helper functions
//
void printUsage()
{
std::cout << "\n" <<
"usage: generate_constants [-o <out file>]" << "\n\n";
}
void printBanner()
{
printBannerLineTop();
std::cout << "// Generating floating point constants" << "\n";
printBannerLineBottom();
}
//
// Top-level functions
//
void parseCommandLine(int argc, char **argv)
{
int opt;
// Retrieve command line options
while ((opt = getopt(argc, argv, "o:")) != -1)
{
switch(opt)
{
case 'o': CMD_outFile = optarg; break;
default: printUsage(); ERROR();
}
}
}
//
// Constants generation
//
std::string toAssemblerString(const std::string &constName, double constValue)
{
uint8_t outFloat[5] = { 0 };
// Retrieve mantissa nad exponent
int exponent = -0x80;
double mantissa = frexp(std::abs(constValue), &exponent);
// Round the mantissa to output format precission
const double coeff = 256.0 * 256.0 * 256.0 * 256.0;
double intPart;
if (modf(mantissa * coeff, &intPart) >= 0.5)
{
mantissa = (intPart + 1.0) / coeff;
}
else
{
mantissa = intPart / coeff;
}
if (mantissa >= 1.0)
{
mantissa = 0.5;
exponent++;
}
// Add a bias to exponent
exponent += 0x80;
// Check if output format can contain such a number
if (exponent > 0xFF)
{
ERROR(std::string("const '") + constName + "' abs value too large");
}
else if (exponent <= 0)
{
ERROR(std::string("const '") + constName + "' abs value too small");
}
// Set output constant exponent
outFloat[0] = exponent;
// Set output constant mantissa
for (uint8_t idxByte = 1; idxByte <= 4; idxByte++)
{
for (uint8_t idxBit = 0; idxBit <= 7; idxBit++)
{
outFloat[idxByte] = outFloat[idxByte] << 1;
if (mantissa >= 0.5)
{
outFloat[idxByte]++;
mantissa -= 0.5;
}
mantissa *= 2;
}
}
if (mantissa > 0.0) ERROR(std::string("const '") + constName + "' export error");
if (outFloat[1] < 0x80) ERROR(std::string("const '") + constName + "' not normalized");
// Set output constant sign
if (constValue >= 0.0)
{
outFloat[1] = outFloat[1] & 0x7F;
}
// Return the output constant as string for assembler
char buf[256] = { 0 };
snprintf(buf, sizeof(buf), "$%02X, $%02X, $%02X, $%02X, $%02X // %22.10f",
outFloat[0],
outFloat[1],
outFloat[2],
outFloat[3],
outFloat[4],
constValue);
std::string outDef = std::string("\n!macro PUT_CONST_") + constName + " {\n\t!byte " + buf + "\n}\n";
return outDef;
}
void writeConstants()
{
// Convert constants to assembler strings
for (auto &entry : GLOBAL_constants)
{
entry.outString = toAssemblerString(entry.constName, entry.constValue);
}
// Remove old file
unlink(CMD_outFile.c_str());
// Open output file for writing
std::ofstream outFile(CMD_outFile, std::fstream::out | std::fstream::trunc);
if (!outFile.good()) ERROR(std::string("can't open oputput file '") + CMD_outFile + "'");
// Write header
outFile << "//\n// Generated file - do not edit\n//\n\n";
// Write constants
for (auto &entry : GLOBAL_constants)
{
outFile << entry.outString;
}
if (!outFile.good())
{
outFile.close();
unlink(CMD_outFile.c_str());
ERROR(std::string("can't write oputput file '") + CMD_outFile + "'");
}
// Close the file
outFile.close();
std::cout << std::string("floating point constants written to: ") + CMD_outFile + "\n\n";
}
//
// Main function
//
int main(int argc, char **argv)
{
parseCommandLine(argc, argv);
printBanner();
writeConstants();
return 0;
}