diff options
author | 2015-11-08 12:56:12 +0100 | |
---|---|---|
committer | 2015-11-08 12:56:12 +0100 | |
commit | 7c19aac60e12d6f5ea301bdb34d7826a01e0b06f (patch) | |
tree | f310d86aa2c6bfc19d115307dedde4eb0cd52dad /src/lib/util/jedparse.cpp | |
parent | a57b46ae933badd7441ce1644711dbb851e2b504 (diff) |
Rename *.c -> *.cpp in our source (nw)
Diffstat (limited to 'src/lib/util/jedparse.cpp')
-rw-r--r-- | src/lib/util/jedparse.cpp | 444 |
1 files changed, 444 insertions, 0 deletions
diff --git a/src/lib/util/jedparse.cpp b/src/lib/util/jedparse.cpp new file mode 100644 index 00000000000..9bed1164303 --- /dev/null +++ b/src/lib/util/jedparse.cpp @@ -0,0 +1,444 @@ +// license:BSD-3-Clause +// copyright-holders:Aaron Giles +/*************************************************************************** + + jedparse.c + + Parser for .JED files into raw fusemaps. + +**************************************************************************** + + Binary file format: + + Offset + 0 = Total number of fuses (32 bits) + 4 = Raw fuse data, packed 8 bits at a time, LSB to MSB + +***************************************************************************/ + +#include <stdio.h> +#include <stdlib.h> +#include <string.h> +#include <ctype.h> +#include "jedparse.h" + + + +/*************************************************************************** + DEBUGGING +***************************************************************************/ + +#define LOG_PARSE 0 + + + +/*************************************************************************** + TYPE DEFINITIONS +***************************************************************************/ + +struct jed_parse_info +{ + UINT16 checksum; /* checksum value */ + UINT32 explicit_numfuses; /* explicitly specified number of fuses */ +}; + + + +/*************************************************************************** + UTILITIES +***************************************************************************/ + +/*------------------------------------------------- + ishex - is a character a valid hex digit? +-------------------------------------------------*/ + +static int ishex(char c) +{ + return (c >= '0' && c <= '9') || (c >= 'A' && c <= 'F'); +} + + + +/*------------------------------------------------- + hexval - the hex value of a given character +-------------------------------------------------*/ + +static int hexval(char c) +{ + return (c >= '0' && c <= '9') ? (c - '0') : (10 + c - 'A'); +} + + + +/*------------------------------------------------- + isdelim - is a character a JEDEC delimiter? +-------------------------------------------------*/ + +static int isdelim(char c) +{ + return (c == ' ' || c == 13 || c == 10); +} + + + +/*------------------------------------------------- + suck_number - read a decimal value from the + character stream +-------------------------------------------------*/ + +static UINT32 suck_number(const UINT8 **psrc) +{ + const UINT8 *src = *psrc; + UINT32 value = 0; + + /* skip delimiters */ + while (isdelim(*src)) + src++; + + /* loop over and accumulate digits */ + while (isdigit(*src)) + { + value = value * 10 + *src - '0'; + src++; + } + + /* return a pointer to the string afterwards */ + *psrc = src; + return value; +} + + + +/*************************************************************************** + CORE IMPLEMENTATION +***************************************************************************/ + +/*------------------------------------------------- + process_field - process a single JEDEC field +-------------------------------------------------*/ + +static void process_field(jed_data *data, const UINT8 *cursrc, const UINT8 *srcend, jed_parse_info *pinfo) +{ + /* switch off of the field type */ + switch (*cursrc) + { + case 'Q': + cursrc++; + switch (*cursrc) + { + /* number of fuses */ + case 'F': + cursrc++; + pinfo->explicit_numfuses = data->numfuses = suck_number(&cursrc); + break; + } + break; + + /* default fuse state (0 or 1) */ + case 'F': + cursrc++; + if (LOG_PARSE) printf("F%c\n", *cursrc); + if (*cursrc == '0') + memset(data->fusemap, 0x00, sizeof(data->fusemap)); + else + memset(data->fusemap, 0xff, sizeof(data->fusemap)); + break; + + /* fuse states */ + case 'L': + { + UINT32 curfuse; + + /* read the fuse number */ + cursrc++; + curfuse = suck_number(&cursrc); + if (LOG_PARSE) printf("L%u\n", curfuse); + + /* read digits, skipping delimiters */ + for ( ; cursrc < srcend; cursrc++) + if (*cursrc == '0' || *cursrc == '1') + { + jed_set_fuse(data, curfuse, *cursrc - '0'); + if (LOG_PARSE) printf(" fuse %u = %d\n", curfuse, 0); + if (curfuse >= data->numfuses) + data->numfuses = curfuse + 1; + curfuse++; + } + break; + } + + /* fuse checksum */ + case 'C': + cursrc++; + if (cursrc < srcend + 4 && ishex(cursrc[0]) && ishex(cursrc[1]) && ishex(cursrc[2]) && ishex(cursrc[3])) + { + pinfo->checksum = 0; + while (ishex(*cursrc) && cursrc < srcend) + pinfo->checksum = (pinfo->checksum << 4) | hexval(*cursrc++); + } + break; + } +} + + + +/*------------------------------------------------- + jed_parse - parse a .JED file that has been + loaded raw into memory +-------------------------------------------------*/ + +int jed_parse(const void *data, size_t length, jed_data *result) +{ + const UINT8 *cursrc = (const UINT8 *)data; + const UINT8 *srcend = cursrc + length; + const UINT8 *scan; + jed_parse_info pinfo; + UINT16 checksum; + int i; + + /* initialize the output and the intermediate info struct */ + memset(result, 0, sizeof(*result)); + memset(&pinfo, 0, sizeof(pinfo)); + + /* first scan for the STX character; ignore anything prior */ + while (cursrc < srcend && *cursrc != 0x02) + cursrc++; + if (cursrc >= srcend) + return JEDERR_INVALID_DATA; + + /* then scan to see if we have an ETX */ + scan = cursrc; + checksum = 0; + while (scan < srcend && *scan != 0x03) + checksum += *scan++ & 0x7f; + if (scan >= srcend) + return JEDERR_INVALID_DATA; + + /* see if there is a transmission checksum at the end */ + checksum += *scan; + if (scan + 4 < srcend && ishex(scan[1]) && ishex(scan[2]) && ishex(scan[3]) && ishex(scan[4])) + { + UINT16 dessum = (hexval(scan[1]) << 12) | (hexval(scan[2]) << 8) | (hexval(scan[3]) << 4) | hexval(scan[4] << 0); + if (dessum != 0 && dessum != checksum) + return JEDERR_BAD_XMIT_SUM; + } + + /* the ETX becomes the real srcend */ + srcend = scan; + + /* blast through the comment field */ + cursrc++; + while (cursrc < srcend && *cursrc != '*') + cursrc++; + + /* now loop over fields and decide which ones go in the file output */ + cursrc++; + while (cursrc < srcend) + { + /* skip over delimiters */ + while (cursrc < srcend && isdelim(*cursrc)) + cursrc++; + if (cursrc >= srcend) + break; + + /* end of field is an asterisk -- find it */ + scan = cursrc; + while (scan < srcend && *scan != '*') + scan++; + if (scan >= srcend) + return JEDERR_INVALID_DATA; + + /* process the field */ + process_field(result, cursrc, scan, &pinfo); + + /* advance past it */ + cursrc = scan + 1; + } + + /* if we got an explicit fuse count, override our computed count */ + if (pinfo.explicit_numfuses != 0) + result->numfuses = pinfo.explicit_numfuses; + + /* clear out leftover bits */ + if (result->numfuses % 8 != 0) + result->fusemap[result->numfuses / 8] &= (1 << (result->numfuses % 8)) - 1; + memset(&result->fusemap[(result->numfuses + 7) / 8], 0, sizeof(result->fusemap) - (result->numfuses + 7) / 8); + + /* validate the checksum */ + checksum = 0; + for (i = 0; i < (result->numfuses + 7) / 8; i++) + checksum += result->fusemap[i]; + if (pinfo.checksum != 0 && checksum != pinfo.checksum) + return JEDERR_BAD_FUSE_SUM; + + return JEDERR_NONE; +} + + + +/*------------------------------------------------- + jed_output - generate a new .JED file based + on the jed_data provided +-------------------------------------------------*/ + +size_t jed_output(const jed_data *data, void *result, size_t length) +{ + UINT8 *curdst = (UINT8 *)result; + UINT8 *dstend = curdst + length; + int i, zeros, ones; + char tempbuf[256]; + UINT16 checksum; + UINT8 defbyte; + UINT8 *temp; + + /* always start the DST with a standard header and an STX */ + tempbuf[0] = 0x02; + sprintf(&tempbuf[1], "JEDEC file generated by jedutil*\n"); + if (curdst + strlen(tempbuf) <= dstend) + memcpy(curdst, tempbuf, strlen(tempbuf)); + curdst += strlen(tempbuf); + + /* append the package information */ + sprintf(tempbuf, "QF%d*\n", data->numfuses); + if (curdst + strlen(tempbuf) <= dstend) + memcpy(curdst, tempbuf, strlen(tempbuf)); + curdst += strlen(tempbuf); + + /* compute the checksum */ + checksum = 0; + for (i = 0; i < data->numfuses / 8; i++) + checksum += data->fusemap[i]; + if (data->numfuses % 8 != 0) + checksum += data->fusemap[data->numfuses / 8] & ((1 << (data->numfuses % 8)) - 1); + + /* determine if we are mostly 0's or mostly 1's */ + for (i = zeros = ones = 0; i < data->numfuses / 8; i++) + if (data->fusemap[i] == 0x00) + zeros++; + else if (data->fusemap[i] == 0xff) + ones++; + defbyte = (ones > zeros) ? 0xff : 0x00; + + /* output the default fuse state */ + sprintf(tempbuf, "F%d*\n", defbyte & 1); + if (curdst + strlen(tempbuf) <= dstend) + memcpy(curdst, tempbuf, strlen(tempbuf)); + curdst += strlen(tempbuf); + + /* now loop over groups of 32 fuses and output non-default groups */ + for (i = 0; i < data->numfuses; i += 32) + if (data->fusemap[i / 8 + 0] != defbyte || + data->fusemap[i / 8 + 1] != defbyte || + data->fusemap[i / 8 + 2] != defbyte || + data->fusemap[i / 8 + 3] != defbyte) + { + int stroffs; + int j; + + /* build up a string of 32 fuses */ + stroffs = sprintf(tempbuf, "L%05d ", i); + for (j = 0; j < 32 && i+j < data->numfuses; j++) + tempbuf[stroffs++] = '0' + jed_get_fuse(data, i + j); + stroffs += sprintf(&tempbuf[stroffs], "*\n"); + + /* append to the buffer */ + if (curdst + strlen(tempbuf) <= dstend) + memcpy(curdst, tempbuf, strlen(tempbuf)); + curdst += strlen(tempbuf); + } + + /* write the checksum */ + sprintf(tempbuf, "C%04X*\n", checksum); + if (curdst + strlen(tempbuf) <= dstend) + memcpy(curdst, tempbuf, strlen(tempbuf)); + curdst += strlen(tempbuf); + + /* now compute the transmission checksum */ + checksum = 0; + for (temp = (UINT8 *)result; temp < curdst && temp < dstend; temp++) + checksum += *temp & 0x7f; + checksum += 0x03; + + /* append the ETX and the transmission checksum */ + tempbuf[0] = 0x03; + sprintf(&tempbuf[1], "%04X", checksum); + if (curdst + strlen(tempbuf) <= dstend) + memcpy(curdst, tempbuf, strlen(tempbuf)); + curdst += strlen(tempbuf); + + /* return the final size */ + return curdst - (UINT8 *)result; +} + + + +/*------------------------------------------------- + jedbin_parse - parse a binary JED file that + has been loaded raw into memory +-------------------------------------------------*/ + +int jedbin_parse(const void *data, size_t length, jed_data *result) +{ + const UINT8 *cursrc = (const UINT8 *)data; + + /* initialize the output */ + memset(result, 0, sizeof(*result)); + + /* need at least 4 bytes */ + if (length < 4) + return JEDERR_INVALID_DATA; + + /* first unpack the number of fuses */ + result->numfuses = (cursrc[0] << 24) | (cursrc[1] << 16) | (cursrc[2] << 8) | cursrc[3]; + cursrc += 4; + if (result->numfuses == 0 || result->numfuses > JED_MAX_FUSES) + return JEDERR_INVALID_DATA; + + /* now make sure we have enough data in the source */ + if (length < 4 + (result->numfuses + 7) / 8) + return JEDERR_INVALID_DATA; + + /* copy in the data */ + memcpy(result->fusemap, cursrc, (result->numfuses + 7) / 8); + return JEDERR_NONE; +} + + + +/*------------------------------------------------- + jedbin_output - generate a new binary JED file + based on the jed_data provided +-------------------------------------------------*/ + +/** + * @fn size_t jedbin_output(const jed_data *data, void *result, size_t length) + * + * @brief Jedbin output. + * + * @param data The data. + * @param [out] result If non-null, the result. + * @param length The length. + * + * @return A size_t. + */ + +size_t jedbin_output(const jed_data *data, void *result, size_t length) +{ + UINT8 *curdst = (UINT8 *)result; + + /* ensure we have enough room */ + if (length >= 4 + (data->numfuses + 7) / 8) + { + /* store the number of fuses */ + *curdst++ = data->numfuses >> 24; + *curdst++ = data->numfuses >> 16; + *curdst++ = data->numfuses >> 8; + *curdst++ = data->numfuses >> 0; + + /* copy in the rest of the data */ + memcpy(curdst, data->fusemap, (data->numfuses + 7) / 8); + } + + /* return the final size */ + return 4 + (data->numfuses + 7) / 8; +} |