summaryrefslogtreecommitdiff
path: root/src
diff options
context:
space:
mode:
authorDimitri Sokolyuk <demon@dim13.org>2010-01-06 17:55:16 +0000
committerDimitri Sokolyuk <demon@dim13.org>2010-01-06 17:55:16 +0000
commit09381c6097773b08622c810428a84840b5b08070 (patch)
treef62afce58620ebbffe6abcf1b8e1d8df6a09a5b8 /src
AVR SP12 programmer
Diffstat (limited to 'src')
-rw-r--r--src/buffer.c691
-rw-r--r--src/device.c434
-rw-r--r--src/dos_cpt.h53
-rw-r--r--src/eeprom.c125
-rw-r--r--src/flash.c292
-rw-r--r--src/init.c854
-rw-r--r--src/makefile58
-rw-r--r--src/sp12.c1136
-rw-r--r--src/sp12.h520
-rw-r--r--src/winnt.c75
10 files changed, 4238 insertions, 0 deletions
diff --git a/src/buffer.c b/src/buffer.c
new file mode 100644
index 0000000..5d37e14
--- /dev/null
+++ b/src/buffer.c
@@ -0,0 +1,691 @@
+/* SP12: A serial programmer for working with Atmel AVR uCs */
+/* Copyright (C) 1997-2003 Ken Huntington, Kevin Towers, Pitronics. */
+
+/* This program 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. */
+
+/* This program 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 this program; if not, write to the Free Software */
+/* Foundation, Inc., 59 Temple Place - Suite 330, Boston, */
+/* MA 02111-1307, USA. */
+
+/* Pitronics can be reached by email: sbolt@xs4all.nl */
+/* Kevin Towers can be reached by email: ktowers@omnexcontrols.com */
+/* Ken Huntington can be reached by email: kenh@compmore.net */
+
+#include <stdlib.h>
+#include <stdio.h>
+#include <string.h>
+#include <time.h>
+#include "sp12.h"
+
+
+/* Converts address or data string to number, automatically */
+/* selecting hex (0xNNN), oct (0NNN), binary (BNNN) or */
+/* dec (NNN) input. */
+
+unsigned int str2num(char *addressOrData) {
+
+ unsigned int number;
+
+ /*
+ * Determine format (hex, decimal or binary) by looking at prefix
+ * and convert data to unsigned int using base 2, 8, 10 or 16
+ */
+ if (addressOrData[1] == 'x')
+ number = (unsigned int) strtol(addressOrData, NULL, 16);
+ else if (addressOrData[0] == '0')
+ number = (unsigned int) strtol(addressOrData, NULL, 8);
+ else if (addressOrData[0] == 'B')
+ number = (unsigned int) strtol(&addressOrData[1], NULL, 2);
+ else
+ number = (unsigned int) strtol(addressOrData, NULL, 10);
+
+ return(number);
+}
+
+/* convert unsigned int to a string-representation of the */
+/* binary number (ascii `0' is 48, ascii `1' is 49) */
+
+void num2bin(unsigned char buffer, char *binary) {
+
+ int Bi;
+
+ for (Bi = 7; Bi >= 0; Bi--) {
+ binary[Bi] = (buffer & 0x01) + 48;
+ buffer = buffer >> 1;
+ }
+ binary[8] = '\0';
+}
+
+/* Reads program file in Atmel generic format (address:data) or */
+/* Intel HEX format; stores data into into flashBuf by address. */
+/* flashBuf by address. The two numbers in the generic format are */
+/* hex without `0x'. For example: 00000c:99e1 */
+/* Note: the 16-bit bufLimit is not suitable for Mega address space. */
+
+int readFlashFile(char flashPath[], unsigned int flashBuf[], long bufLimit) {
+
+ FILE *flashBufPtr;
+ char wordstring[WORDLEN];
+ long address;
+ unsigned int data;
+ char buffer[MAXLEN];
+ long offsetAddress = 0L;
+ int dataByteCount;
+ unsigned int lineAddress;
+ int recordType;
+ int idx;
+ int checksum;
+ int tmp;
+
+ if ((flashBufPtr = fopen(flashPath, "r")) == NULL) {
+ return(1);
+ } else {
+ if (fgets(wordstring, WORDLEN, flashBufPtr) != NULL) {
+ /*
+ * Try Atmel generic format (default of the Atmel assembler).
+ */
+ if (wordstring[FLASH_DATAPTR] == ':') {
+ fseek(flashBufPtr, 0, SEEK_SET);
+ while (fgets(wordstring, WORDLEN, flashBufPtr) != NULL) {
+ sscanf(wordstring, "%lx", &address);
+ if (wordstring[FLASH_DATAPTR] != ':') {
+ return(1);
+ }
+ sscanf(&wordstring[FLASH_DATAPTR + 1], "%x", &data);
+ if (address < bufLimit)
+ flashBuf[address] = data;
+ else
+ return(1);
+ }
+ fclose(flashBufPtr);
+ return(0);
+ }
+ /*
+ * Try Intel HEX format.
+ */
+ if (wordstring[0] == ':') {
+ fseek(flashBufPtr, 0, SEEK_SET);
+ while (fgets(buffer, MAXLEN, flashBufPtr) != NULL) {
+ checksum = 0;
+ sscanf(&buffer[1], "%2x", &dataByteCount);
+ checksum += dataByteCount;
+ sscanf(&buffer[3], "%4x", &lineAddress);
+ sscanf(&buffer[3], "%2x", &tmp);
+ checksum += tmp;
+ sscanf(&buffer[5], "%2x", &tmp);
+ checksum += tmp;
+ sscanf(&buffer[7], "%2x", &recordType);
+ checksum += recordType;
+ /*
+ * Record type 02 means update offset address
+ */
+ if (recordType == 2) {
+ sscanf(&buffer[9], "%4lx", &offsetAddress);
+ sscanf(&buffer[9], "%2x", &tmp);
+ checksum += tmp;
+ sscanf(&buffer[11], "%2x", &tmp);
+ checksum += tmp;
+ checksum = (unsigned char) (0x100 - (unsigned char) checksum);
+ sscanf(&buffer[13], "%2x", &tmp);
+ if (checksum != tmp)
+ return(1);
+ }
+ if (recordType == 0) {
+ for (idx = 9; idx < 9 + dataByteCount * 2; idx += 2) {
+ sscanf(&buffer[idx], "%2x", &data);
+ /*
+ * Use word addresses for the AT90 2-byte data format
+ */
+ address = lineAddress / 2;
+ address = address + (offsetAddress * 0x10) / 2;
+ if (address < bufLimit) {
+ if (lineAddress & 0x0001) /* is it an odd address? */
+ /* then set MSB of data */
+ flashBuf[address] &= (data << 8) | 0x00ff;
+ else /* else set LSB of data */
+ flashBuf[address] &= data | 0xff00;
+ } else {
+ return(1);
+ }
+ lineAddress++;
+ checksum += data;
+ }
+ checksum = (unsigned char) (0x100 - (unsigned char) checksum);
+ sscanf(&buffer[idx], "%2x", &tmp);
+ if (checksum != tmp)
+ return(1);
+ }
+ }
+ fclose(flashBufPtr);
+ return(0);
+ }
+ }
+ }
+ return(1);
+}
+
+/* Reads eeprom file in format address:data, stores data into */
+/* eepromBuf by address. Both numbers can be hexadecimal (0xC4), */
+/* decimal (196), octal (0304) or binary (B11000100). */
+
+int readEepromFile(char eepromPath[], unsigned int eepromBuf[], long bufLimit) {
+
+ FILE *eepromBufPtr;
+ char wordstring[WORDLEN];
+ long address;
+ unsigned int data;
+ char buffer[MAXLEN];
+ char *dataPtr;
+ long offsetAddress = 0L;
+ int dataByteCount;
+ unsigned int lineAddress;
+ int recordType;
+ int idx;
+ int checksum;
+ int tmp;
+
+ if ((eepromBufPtr = fopen(eepromPath, "r")) == NULL) {
+ return(1);
+ } else {
+ if (fgets(wordstring, WORDLEN, eepromBufPtr) != NULL) {
+ /*
+ * Try sp12 eeprom format.
+ */
+ if (strchr(wordstring, ':') != &wordstring[0]) {
+ fseek(eepromBufPtr, 0, SEEK_SET);
+ while (fgets(wordstring, WORDLEN, eepromBufPtr) != NULL) {
+ if ((dataPtr = strchr(wordstring, ':')) == NULL) {
+ return(1);
+ }
+ dataPtr[0] = '\0';
+ address =str2num(wordstring);
+ data = str2num(++dataPtr);
+ if (address < bufLimit)
+ eepromBuf[address] = data;
+ else
+ return(1);
+ }
+ fclose(eepromBufPtr);
+ return(0);
+ }
+ /*
+ * Try Intel HEX format.
+ */
+ if (strchr(wordstring, ':') == &wordstring[0]) {
+ fseek(eepromBufPtr, 0, SEEK_SET);
+ while (fgets(buffer, MAXLEN, eepromBufPtr) != NULL) {
+ checksum = 0;
+ sscanf(&buffer[1], "%2x", &dataByteCount);
+ checksum += dataByteCount;
+ sscanf(&buffer[3], "%4x", &lineAddress);
+ sscanf(&buffer[3], "%2x", &tmp);
+ checksum += tmp;
+ sscanf(&buffer[5], "%2x", &tmp);
+ checksum += tmp;
+ sscanf(&buffer[7], "%2x", &recordType);
+ checksum += recordType;
+ /*
+ * Record type 02 means update offset address
+ */
+ if (recordType == 2) {
+ sscanf(&buffer[9], "%4lx", &offsetAddress);
+ sscanf(&buffer[9], "%2x", &tmp);
+ checksum += tmp;
+ sscanf(&buffer[11], "%2x", &tmp);
+ checksum += tmp;
+ checksum = (unsigned char) (0x100 - (unsigned char) checksum);
+ sscanf(&buffer[13], "%2x", &tmp);
+ if (checksum != tmp)
+ return(1);
+ }
+ if (recordType == 0) {
+ address = lineAddress;
+ address = address + (offsetAddress * 0x10) / 2;
+ /*
+ * Use byte addresses for single byte eeprom data format.
+ */
+ for (idx = 9; idx < 9 + dataByteCount * 2; idx += 2) {
+ sscanf(&buffer[idx], "%2x", &data);
+ if (address < bufLimit)
+ eepromBuf[address] = data;
+ else
+ return(1);
+ address++;
+ checksum += data;
+ }
+ checksum = (unsigned char) (0x100 - (unsigned char) checksum);
+ sscanf(&buffer[idx], "%2x", &tmp);
+ if (checksum != tmp)
+ return(1);
+ }
+ }
+ fclose(eepromBufPtr);
+ return(0);
+ }
+ }
+ }
+ return(1);
+}
+
+/* Calculate checksum over buffer[]; int bufLimit is number of */
+/* elements in buffer. */
+
+unsigned int checksum(unsigned int buffer[], long bufLimit) {
+
+ long idx;
+ unsigned int chksum = 0;
+
+ /*
+ * cyclic right shift 1 of chksum + buffer element
+ */
+ for (idx = 0; idx < bufLimit; idx++) {
+ if (buffer[idx] == 0xfff)
+ buffer[idx] = 0xff;
+ chksum = ((chksum>>1) + ((chksum & 1) ?0x8000:0) + buffer[idx]) & 0xffff;
+ }
+ return(chksum);
+}
+
+/* Routines to write the flash or eeprom buffer into a file, */
+/* using Intel HEX format or as a hex dump with ascii translation. */
+
+#define BYTES_PER_LINE 16
+
+static int flash_intel_hex (FILE *filePtr, unsigned int *buffer,
+ long addr, long length) {
+
+ int line_cnt = 0; /* line byte count */
+ int line_len = 0; /* expected line length */
+ unsigned char checksum = 0;
+ unsigned char byt;
+
+ length *= 2;
+ while (length > 0) {
+ if (addr > 0xFFFF) { /* add record type 02 */
+ fprintf (filePtr, ":020000021000EC\n");
+ addr = 0x10000 - addr;
+ }
+ if (line_cnt == 0) { /* write line header */
+ line_len = (length > BYTES_PER_LINE) ? BYTES_PER_LINE : length;
+ fprintf (filePtr, ":%02X%04lX00", line_len, addr);
+ checksum = line_len;
+ checksum += addr & 0xff;
+ checksum += addr >> 8;
+ }
+
+ if (addr & 0x0001) { /* is it an odd address? */
+ byt = *buffer >> 8; /* write MSB */
+ buffer++;
+ } else {
+ byt = *buffer & 0x00ff; /* write LSB */
+ }
+ fprintf (filePtr, "%02X", byt);
+ checksum += byt;
+
+ if (++line_cnt == line_len) { /* write checksum */
+ fprintf (filePtr, "%02X\n", -checksum & 0xff);
+ line_cnt = 0;
+ }
+ addr++;
+ length--;
+ }
+ fprintf (filePtr, ":00000001FF\n"); /* write the terminator */
+ return (0);
+}
+
+static int eeprom_intel_hex (FILE *filePtr, unsigned int *buffer,
+ unsigned int addr, long length) {
+ int line_cnt = 0; /* line byte count */
+ int line_len = 0; /* expected line length */
+ unsigned char checksum = 0;
+
+ while (length > 0) {
+ if (line_cnt == 0) { /* write line header */
+ line_len = (length > BYTES_PER_LINE) ? BYTES_PER_LINE : length;
+ fprintf (filePtr, ":%02X%04X00", line_len, addr);
+ checksum = line_len;
+ checksum += addr & 0xff;
+ checksum += addr >> 8;
+ }
+ fprintf (filePtr, "%02X", *buffer);
+ checksum += *buffer;
+ if (++line_cnt == line_len) { /* write checksum */
+ fprintf (filePtr, "%02X\n", -checksum & 0xff);
+ line_cnt = 0;
+ }
+ buffer++;
+ addr++;
+ length--;
+ }
+ fprintf (filePtr, ":00000001FF\n"); /* write the terminator */
+ return (0);
+}
+
+int file_hex_ascii(FILE *filePtr, unsigned int buffer[],
+ int bufLimit, int twoByteFlag) {
+
+ unsigned long address;
+ char dataStr[MAXLEN];
+ char asciiStr[MAXLEN];
+ char lineBuf[MAXLEN];
+ unsigned char loByte, hiByte;
+ int addressPerLine = 0x10;
+
+ dataStr[0] = '\0';
+ asciiStr[0] = '\0';
+ for (address = 0; address < bufLimit; address++) {
+ if ((address > 0) && (address % addressPerLine) == 0) {
+ if ((fprintf(filePtr, "%06lx %s %s\n",
+ address - addressPerLine, dataStr, asciiStr)) == EOF)
+ return(1);
+ dataStr[0] = '\0';
+ asciiStr[0] = '\0';
+ }
+ if (twoByteFlag) {
+ addressPerLine = 0x08;
+ loByte = (unsigned char) buffer[address];
+ hiByte = (unsigned char) (buffer[address] >> 8);
+ sprintf(lineBuf, "%02x %02x ", hiByte, loByte);
+ strcat(dataStr, lineBuf);
+ if ((hiByte > 31) && (hiByte < 127))
+ sprintf(lineBuf, "%c", hiByte);
+ else
+ sprintf(lineBuf, ".");
+ strcat(asciiStr, lineBuf);
+ if ((loByte > 31) && (loByte < 127))
+ sprintf(lineBuf, "%c", loByte);
+ else
+ sprintf(lineBuf, ".");
+ strcat(asciiStr, lineBuf);
+ } else {
+ sprintf(lineBuf, "%02x ", buffer[address]);
+ strcat(dataStr, lineBuf);
+ if ((buffer[address] > 31) && (buffer[address] < 127))
+ sprintf(lineBuf, "%c", buffer[address]);
+ else
+ sprintf(lineBuf, ".");
+ strcat(asciiStr, lineBuf);
+ }
+ }
+ if ((fprintf(filePtr, "%06lx %s %s\n",
+ address - addressPerLine, dataStr, asciiStr)) == EOF)
+ return(1);
+ fprintf(filePtr, "Checksum: %04x\n",
+ checksum(buffer, bufLimit));
+ return(0);
+}
+
+/* Writes flashBuf[] into FILE *flashBufPtr using either Intel HEX */
+/* or the `native' format. */
+/* If write to file fails, return(1), else return(0) */
+
+int fileFlashBuf(unsigned int flashBuf[], long bufLimit,
+ char flashPath[], int intel_flag, int hex_ascii_flag) {
+
+ FILE *flashBufPtr;
+ long address;
+
+ if ((flashBufPtr = fopen(flashPath, "w")) == NULL)
+ return(1);
+
+ if (hex_ascii_flag) {
+ file_hex_ascii(flashBufPtr, flashBuf, bufLimit, 1);
+ } else if (intel_flag) {
+ flash_intel_hex(flashBufPtr, flashBuf, 0, bufLimit);
+ } else {
+ for (address = 0; address < bufLimit; address++) {
+ if ((fprintf(flashBufPtr, "%06lx:%04x\n",
+ address, flashBuf[address])) == EOF)
+ return(1);
+ }
+ }
+ fclose(flashBufPtr);
+ return(0);
+}
+
+/* Writes eepromBuf[] into FILE *eepromBufPtr; */
+/* If write to file fails, return(1), else return(0) */
+
+int fileEepromBuf(unsigned int eepromBuf[], long bufLimit,
+ char eepromPath[], int intel_flag, int hex_ascii_flag) {
+
+ FILE *eepromBufPtr;
+ long address;
+ char binary[9] = "76543210";
+
+ if ((eepromBufPtr = fopen(eepromPath, "w")) == NULL)
+ return(1);
+
+ if (hex_ascii_flag) {
+ file_hex_ascii(eepromBufPtr, eepromBuf, bufLimit, 0);
+ } else if (intel_flag) {
+ eeprom_intel_hex(eepromBufPtr, eepromBuf, 0, bufLimit);
+ } else {
+ if ((fprintf(eepromBufPtr,
+ "Address: Data in hex, dec, oct, bin\n")) == EOF)
+ return(1);
+ for (address = 0; address < bufLimit; address++) {
+ num2bin((unsigned char) eepromBuf[address], binary);
+ if ((fprintf(eepromBufPtr,
+ " %#06lx: %#04x %3d %#4o %s\n",
+ address, eepromBuf[address], eepromBuf[address],
+ eepromBuf[address], binary)) == EOF)
+ return(1);
+ }
+ }
+ fclose(eepromBufPtr);
+ return(0);
+}
+
+/* Writes buffer[] to stdout as a hex dump with ascii translation */
+
+void printBuffer(unsigned int buffer[], long bufLimit, int twoByteFlag) {
+
+ long address;
+ char dataStr[MAXLEN];
+ char asciiStr[MAXLEN];
+ char lineBuf[MAXLEN];
+ unsigned char loByte, hiByte;
+ int addressPerLine = 0x10;
+
+ dataStr[0] = '\0';
+ asciiStr[0] = '\0';
+ for (address = 0; address < bufLimit; address++) {
+ if ((address > 0) && (address % addressPerLine) == 0) {
+ printf("%06lx %s %s\n", address - addressPerLine, dataStr, asciiStr);
+ dataStr[0] = '\0';
+ asciiStr[0] = '\0';
+ }
+ if (twoByteFlag) {
+ addressPerLine = 0x08;
+ loByte = (unsigned char) buffer[address];
+ hiByte = (unsigned char) (buffer[address] >> 8);
+ sprintf(lineBuf, "%02x %02x ", hiByte, loByte);
+ strcat(dataStr, lineBuf);
+ if ((hiByte > 31) && (hiByte < 127))
+ sprintf(lineBuf, "%c", hiByte);
+ else
+ sprintf(lineBuf, ".");
+ strcat(asciiStr, lineBuf);
+ if ((loByte > 31) && (loByte < 127))
+ sprintf(lineBuf, "%c", loByte);
+ else
+ sprintf(lineBuf, ".");
+ strcat(asciiStr, lineBuf);
+ } else {
+ sprintf(lineBuf, "%02x ", buffer[address]);
+ strcat(dataStr, lineBuf);
+ if ((buffer[address] > 31) && (buffer[address] < 127))
+ sprintf(lineBuf, "%c", buffer[address]);
+ else
+ sprintf(lineBuf, ".");
+ strcat(asciiStr, lineBuf);
+ }
+ }
+ printf("%06lx %s %s\n", address - addressPerLine, dataStr, asciiStr);
+ printf("Checksum: %04x\n", checksum(buffer, bufLimit));
+}
+
+/* Add spaces until lenght or max is reached */
+/* Try to break line if necessary */
+
+void formatStr(char *logStr, int length, int max) {
+
+ int idx;
+ int limit;
+ int idxL = 0;
+
+ limit = length;
+ for (idx = 0; idx < limit; idx++) {
+ if (idx == max) {
+ logStr[idx] = '\0';
+ break;
+ }
+ if ((++idxL > length / 2) && logStr[idx] == ' ') {
+ logStr[idx] = '\n';
+ limit = limit + idxL;
+ idxL = 0;
+ }
+ if (logStr[idx] == '\0') {
+ logStr[idx] = ' ';
+ logStr[idx + 1] = '\0';
+ }
+ }
+}
+
+/* Add a line to the log about a write command */
+
+int logWrites(char *commandStr, unsigned int address, int data,
+ char *path, unsigned int buffer[], int overlayFlag, int *error) {
+
+ FILE *logPtr;
+ time_t curTime;
+ struct tm *locTime;
+ unsigned int areaChksum = 0;
+ unsigned int *queryBuf;
+ int idx;
+ long adrsCnt;
+ char deviceName[MAXLEN];
+ char fileName[MAXLEN];
+ char *fileNamePtr;
+
+ if (log.logging != 1)
+ return(0);
+
+ formatStr(commandStr, 6, MAXLEN);
+ strcpy(deviceName, device.name);
+ if (strcmp(deviceName, "AT90(L)S2343 or Tiny22(L)") == 0)
+ strcpy(deviceName, "2343/Tiny22");
+ formatStr(deviceName, 12, MAXLEN);
+ queryBuf = malloc(FLASHBUF_UPPERLIMIT * sizeof (unsigned int));
+ if (queryBuf == NULL) {
+ printf ("ERROR: No memory available for buffer!\n");
+ return(0);
+ }
+
+ curTime = time(NULL);
+ locTime = localtime(&curTime);
+
+ if ((logPtr = fopen(log.logPath, "a")) == NULL) {
+ free(queryBuf);
+ return(1);
+ }
+ fprintf(logPtr, "%s %s ", deviceName, commandStr);
+ if (strchr(commandStr, 'f') != NULL) {
+ strcpy(fileName, path);
+ fileNamePtr = fileName;
+ idx = strlen(fileName);
+ while (idx-- > 0 && fileName[idx] != '\\' && fileName[idx] != '/')
+ fileNamePtr = &fileName[idx];
+ formatStr(fileNamePtr, 14, MAXLEN - idx);
+ fprintf(logPtr, "%s ", fileNamePtr);
+ if (log.query) {
+ if (strchr(commandStr, 'p') != NULL) {
+ readFlashArea(queryBuf, device.flashLimit);
+ areaChksum = checksum(queryBuf, device.flashLimit);
+ *error = 0;
+ for (adrsCnt = 0; adrsCnt < device.flashLimit; adrsCnt++) {
+ if (overlayFlag && buffer[adrsCnt] == 0xffff)
+ continue;
+ if (queryBuf[adrsCnt] != buffer[adrsCnt]) {
+ *error = 1;
+ break;
+ }
+ }
+ } else {
+ readEepromArea(queryBuf, device.eepromLimit);
+ areaChksum = checksum(queryBuf, device.eepromLimit);
+ *error = 0;
+ for (adrsCnt = 0; adrsCnt < device.eepromLimit; adrsCnt++) {
+ if (overlayFlag && buffer[adrsCnt] > 0xFF)
+ continue;
+ if (queryBuf[adrsCnt] != (unsigned char) buffer[adrsCnt]) {
+ *error = 1;
+ break;
+ }
+ }
+ }
+ if (*error)
+ fprintf(logPtr, "readback ERROR %s", asctime(locTime));
+ else
+ fprintf(logPtr, "checksum %04x %s", areaChksum, asctime(locTime));
+ } else {
+ fprintf(logPtr, "not queried %s", asctime(locTime));
+ }
+ } else {
+ if (strchr(commandStr, 'p') != NULL)
+ fprintf(logPtr, "%08x:%#06x ", address, data);
+ else
+ fprintf(logPtr, "%08x:%#04x ", address, data);
+ if (*error)
+ fprintf(logPtr, "readback ERROR %s", asctime(locTime));
+ else
+ fprintf(logPtr, "verified %s", asctime(locTime));
+ }
+ fclose(logPtr);
+ free(queryBuf);
+ return(0);
+}
+
+/* Add a line to the log about a lock command */
+
+int logLocks(unsigned int buffer[], char *lockBits) {
+
+ FILE *logPtr;
+ time_t curTime;
+ struct tm *locTime;
+ unsigned int areaChksum = 0;
+ char deviceName[MAXLEN];
+ char lBits[MAXLEN] = "";
+
+ if (log.logging != 1)
+ return(0);
+
+ sprintf(lBits, "%02lx", strtol(lockBits, NULL, 2));
+
+ strcpy(deviceName, device.name);
+ formatStr(deviceName, 12, MAXLEN);
+ curTime = time(NULL);
+ locTime = localtime(&curTime);
+
+ if ((logPtr = fopen(log.logPath, "a")) == NULL)
+ return(1);
+ fprintf(logPtr, "%s -L%s ", deviceName, lBits);
+ readFlashArea(buffer, device.flashLimit);
+ areaChksum = checksum(buffer, device.flashLimit);
+ fprintf(logPtr, "flash %04x ", areaChksum);
+ readEepromArea(buffer, device.eepromLimit);
+ areaChksum = checksum(buffer, device.eepromLimit);
+ fprintf(logPtr, "eeprom %04x %s", areaChksum, asctime(locTime));
+ fclose(logPtr);
+ return(0);
+}
diff --git a/src/device.c b/src/device.c
new file mode 100644
index 0000000..53fe054
--- /dev/null
+++ b/src/device.c
@@ -0,0 +1,434 @@
+/* SP12: A serial programmer for working with Atmel AVR uCs */
+/* Copyright (C) 1997-2008 Ken Huntington, Kevin Towers, */
+/* Artur Pundsack, Pitronics. */
+
+/* This program 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. */
+
+/* This program 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 this program; if not, write to the Free Software */
+/* Foundation, Inc., 59 Temple Place - Suite 330, Boston, */
+/* MA 02111-1307, USA. */
+
+/* Pitronics can be reached by email: sbolt@xs4all.nl */
+/* Kevin Towers can be reached by email: ktowers@omnexcontrols.com */
+/* Ken Huntington can be reached by email: kenh@compmore.net */
+/* Artur Pundsack can be reached by email: ap@pa-tec.de */
+
+#include <stdlib.h>
+#include <stdio.h>
+#include <string.h>
+#include "dos_cpt.h"
+#include "sp12.h"
+
+/* Delay by executing not quite empty loops */
+
+void loopDelay(unsigned long loops) {
+
+ unsigned long Ti;
+ float dummy;
+
+ for (Ti = 0; Ti < loops; Ti++) {
+ dummy = rand();
+ }
+}
+
+/* Assume the device connected to the Centronics port is powered, */
+/* but that device sck was not held low during power-up, so a */
+/* reset pulse is needed before the SPI can be enabled. */
+/* Assume also that power has yet to be switched on (for separate */
+/* programmer powered by parallel port, instead of in-circuit */
+/* programming. */
+/* If uC is 1200, emit just one PROGRAM_ENABLE command. */
+/* Else retry until the third byte read back by clockOutCommand() */
+/* is 0x53, or at least 32 retries have been unsuccessful. */
+
+int enableSPI(int uC) {
+
+ unsigned long readBack = 0;
+ int tries = 0;
+ /*
+ * Initialise programming lines.
+ */
+ bit_clear(outbyte, ENABLE | SCK | MOSI | RESET);
+ outportb(portAddress, outbyte);
+ /*
+ * Delay to make sure capacitor on separate programmer is charged.
+ */
+ delay(timeConst.powerOn);
+ /*
+ * If the resetPuls time constant > 0, then Hi pulse device reset
+ */
+ if (timeConst.resetPuls > 0) {
+ bit_set(outbyte, RESET);
+ outportb(portAddress, outbyte);
+ /*
+ * Delay to make sure that a capacitor
+ * from reset to gnd is (dis)charged
+ */
+ loopDelay(timeConst.resetPuls);
+ bit_clear(outbyte, RESET);
+ outportb(portAddress, outbyte);
+ /*
+ * Delay before program (SPI) enable command
+ */
+ delay(timeConst.programEnable);
+ }
+ /*
+ * Command program enable and check synchronization
+ */
+ while (tries < 33) {
+ tries++;
+ readBack = clockOutCommand(PROGRAM_ENABLE);
+ readBack = (readBack & 0x0000FF00L) >> 8;
+ if ((readBack == 0x53) || (uC == 1200)) {
+ break;
+ } else {
+ /*
+ * Pulse SCK lo-hi-lo
+ */
+ bit_clear(outbyte, SCK);
+ outportb(portAddress, outbyte);
+ loopDelay(timeConst.sckLO);
+ bit_set(outbyte, SCK);
+ outportb(portAddress, outbyte);
+ loopDelay(timeConst.sckHI);
+ bit_clear(outbyte, SCK);
+ outportb(portAddress, outbyte);
+ loopDelay(timeConst.sckLO);
+ }
+ }
+ if (tries > 1)
+ printf("Sp12 tried %d times to find a working device.\n", tries);
+ if ((readBack != 0x53) && (uC != 1200))
+ printf("No device connected.\n");
+/* printf("readBack: %#lx, tries: %d\n", readBack, tries); */
+ return(tries);
+}
+
+/* Adapt Sck timing to user's clock speed setting */
+/* (Init sets the timing equal to sck0.5M, and the flag will be */
+/* zero unless altered by the command line.) */
+
+float setSckTiming(float clkSpeed) {
+
+ float signal;
+
+ clkSpeed = (float) 2 / (clkSpeed * 1e6);
+ if (clkSpeed > timeConst.port)
+ timeConst.sckLO = (unsigned long) (timeConst.loop *
+ (clkSpeed - timeConst.port));
+ else
+ timeConst.sckLO = 1;
+ if (strcmp(device.name, "AT90S1200(A)") == 0
+ && (clkSpeed * 2) > timeConst.port)
+ timeConst.sckHI = (unsigned long) (timeConst.loop *
+ (clkSpeed * 2 - timeConst.port));
+ else
+ timeConst.sckHI = timeConst.sckLO;
+// printf("sckLO %ld, sckHI %ld\n", timeConst.sckLO, timeConst.sckHI);
+ signal = 1.8e-6 / clkSpeed;
+ if (strcmp(device.name, "AT90S1200(A)") == 0) {
+ if ((signal / 2) > (1.8e-6 / timeConst.port))
+ signal = 4.0e-6 / timeConst.port;
+ } else {
+ if (signal > (1.8e-6 / timeConst.port))
+ signal = 2.0e-6 / timeConst.port;
+ }
+ return(signal);
+}
+
+/* Before writing to the flash area, the device has to be erased. */
+/* Note: This command also erases the eeprom area (all to 0xFF). */
+/* There is no need for general erase before writing to the */
+/* eeprom area, since the device has an auto-erase built into */
+/* the eeprom write cycle. Thus you can alter one or more eeprom */
+/* addresses without disturbing the rest of the device. */
+
+void eraseDevice(void) {
+
+ clockOutCommand(CHIP_ERASE);
+ /*
+ * Delay before program (SPI) enable sequence
+ */
+ delay(timeConst.chipErase);
+ /*
+ * syncronisation must be ok, so just one PROGRAM_ENABLE
+ * command will be clocked out.
+ */
+ enableSPI(1200);
+}
+
+/* A routine to switch control the optional test bits on the */
+/* parallel port. */
+
+void portControl(int portFlag, int exitFlag) {
+
+ if (exitFlag) {
+ outbyte = (unsigned char) portFlag;
+ } else {
+ outbyte = (unsigned char) portFlag & PORTACTIVE;
+ delay(10);
+ }
+ outportb(portAddress, outbyte);
+}
+
+/* Reads flash and eeprom area's. Return 1 if a flash address does */
+/* not contain 0xFFFF, or 3 if an eeprom address also isn't blank; */
+/* return 2 if the flash is blank, but the eeprom isn't; */
+/* return 0 is both area's are blank. */
+/* Power bits prior state assumed on; left on at return. */
+/* SCK prior state assumed lo; left lo at return. */
+/* AP, 15.01.2008: - Set the extended adress byte if necessary */
+
+int blankCheck(long flashLimit, long eepromLimit) {
+
+ long address, writeCmd;
+ int signal = 0;
+
+ if (flashLimit > 0x0000FFFF){
+ writeCmd = LOAD_EXT_ADDR;
+ clockOutCommand(writeCmd);
+ }
+
+ for (address = 0; address < flashLimit; address++) {
+ if (readCodeWord(address) != 0xffff) {
+ signal |= 0x01;
+ break; /* arrea is not blank; exit loop */
+ }
+ }
+ for (address = 0; address < eepromLimit; address++) {
+ if (readDataByte(address) != 0xff) {
+ signal |= 0x02;
+ break; /* arrea is not blank; exit loop */
+ }
+ }
+ return(signal);
+}
+
+
+/* Sends a lock or fuses string to the microcontroller. */
+/* Returns 9 if the writeCommand is zero (not available according */
+/* to _sp12dev). */
+/* The lock or fuses string may represent a binary or hex number. */
+/* If it's binary, the length of the string is compared with */
+/* W_FUSESLEN; a useful sanity check. The function returns 2 if */
+/* the length is wrong. A hex number is not checked, as there is no */
+/* way to know how many leading zeroes are intended. */
+/* Next, the block is ored into the writeCommand and uploaded to */
+/* the device. */
+/* Since (re)enabling brown-out detection usually causes the */
+/* AT90(L)S2333/4433 to hang, the fuses are only written, not */
+/* verified. */
+
+int writeFuses(unsigned long commandHM, unsigned long commandLM, \
+ char *fuses, int WfusesLsb, int WfusesLen) {
+
+ unsigned long fuseWord = 0;
+ int Bi;
+
+ if (!commandHM)
+ return(9);
+ if (fuses[1] == 'x')
+ fuseWord = strtoul(fuses, NULL, 16);
+ else if (strlen(fuses) != WfusesLen)
+ return(2);
+ else
+ fuseWord = strtoul(fuses, NULL, 2);
+ fuseWord = fuseWord << WfusesLsb;
+ fuseWord = fuseWord & commandHM;
+ fuseWord = fuseWord | commandLM;
+// printf("\nfuseword %04lx\n", fuseWord);
+ clockOutCommand(fuseWord);
+ /*
+ * Read back the fuses as a binary string
+ */
+ fuseWord = fuseWord >> WfusesLsb;
+ for (Bi = WfusesLen - 1; Bi >= 0; Bi--) {
+ fuses[Bi] = (fuseWord & 0x01L) + 48;
+ fuseWord = fuseWord >> 1;
+ }
+ fuses[WfusesLen] = '\0';
+ /*
+ * ByteWrite delay not long enough to allow reading back
+ * of true ATmega103/603 fuses directly after this
+ */
+ delay(timeConst.chipErase);
+ return(0);
+}
+
+
+/* Reads the lock or fuse bit block from the microcontroller. */
+/* Returns 9 if the readCommand is zero (not available according */
+/* to _sp12dev). */
+
+int readFuses(unsigned long readCommand, char *fuses, \
+ int RfusesLsb, int RfusesLen) {
+
+ unsigned long fuseWord = 0;
+ int Bi;
+
+ if (!readCommand)
+ return(9);
+
+ fuseWord = clockOutCommand(readCommand);
+ fuseWord = fuseWord >> RfusesLsb;
+ for (Bi = RfusesLen - 1; Bi >= 0; Bi--) {
+ fuses[Bi] = (fuseWord & 0x01L) + 48;
+ fuseWord = fuseWord >> 1;
+ }
+ fuses[RfusesLen] = '\0';
+ return(0);
+}
+
+
+/* Read the signature bits from the device attached to the */
+/* Centronics parallel port. */
+
+void readDeviceCode(void) {
+
+ unsigned long readCmd;
+
+ readCmd = READ_DEVICE | 0x0200L;
+ device.sigByte_2 = (unsigned char) clockOutCommand(readCmd);
+ readCmd = READ_DEVICE | 0x0100L;
+ device.sigByte_1 = (unsigned char) clockOutCommand(readCmd);
+ readCmd = READ_DEVICE | 0x0000L;
+ device.sigByte_0 = (unsigned char) clockOutCommand(readCmd);
+}
+
+
+/* Shifts the command highest bit first into unsigned char output, */
+/* which then is used to clock them out bit by bit through */
+/* Centronics data-7. */
+/* Reads back and returns whatever the uC clocks out on Miso */
+/* while the command is clocked in. */
+/* Power bits prior state assumed on; left on at return. */
+/* SCK prior state assumed lo; left lo at return. */
+
+unsigned long clockOutCommand(unsigned long command) {
+
+ unsigned long inBuf;
+ unsigned long readBack = 0x0L;
+ unsigned char inbyte; /* from Centronics status reg. */
+ int mostSigBit;
+ int i;
+
+ mostSigBit = 31;
+ /*
+ * Bits mostSigBit-0 to be clocked out
+ */
+ for (i = mostSigBit; i >= 0; i--) {
+ /*
+ * Bits mostSigBit-0 to be clocked out
+ */
+ if ((command >> i) & 0x01L)
+ bit_set(outbyte, MOSI);
+ else
+ bit_clear(outbyte, MOSI);
+ bit_clear(outbyte, SCK);
+ outportb(portAddress, outbyte);
+ /*
+ * Minimum delay SCK lo period
+ */
+ loopDelay(timeConst.sckLO);
+ /*
+ * Set bit-0 (SCK) hi, leave all other bits as they are
+ */
+ bit_set(outbyte, SCK);
+ outportb(portAddress, outbyte);
+ /*
+ * Minimum delay SCK hi period
+ */
+ loopDelay(timeConst.sckHI);
+ /*
+ * Fetch parallel port status register, clear all bits
+ * except 7 (MOSI), invert, shift and or into buffer
+ */
+ inbyte = inportb(portStatus);
+ inBuf = ((unsigned long) (inbyte ^ MISO_INV)) >> MISO_BITNR;
+ inBuf = (inBuf & 0x00000001L) << i;
+ readBack = readBack | inBuf;
+ }
+ /*
+ * return SCK to lo
+ */
+ bit_clear(outbyte, SCK);
+ outportb(portAddress, outbyte);
+ return(readBack);
+}
+
+/* Accepts a dataByte and a 16-bit address. Writes the byte into */
+/* the device connected to the Centronics port and verifies arrival. */
+/* Returns 1 if verify fails three times, else 0. */
+/* The time constant byteWrite is adjusted dynamically, */
+/* if the device is not a 1200(A) and/or optimizeFlag allows. */
+/* Power bits prior state assumed on; left on at return. */
+/* SCK prior state assumed lo; left lo at return. */
+
+int writeByteVerified(unsigned long writeCommand,
+ unsigned long readCommand, int optimizeFlag) {
+
+ int idx;
+ unsigned char readBack;
+ int failure = 0;
+ unsigned char dataByte;
+ /*
+ * First check if the data isn't already in the uC
+ */
+ dataByte = (unsigned char) writeCommand;
+ readBack = (unsigned char) clockOutCommand(readCommand);
+ if (readBack == dataByte)
+ return(failure);
+ /*
+ * Else write the data into the uC
+ */
+ dataByte = (unsigned char) writeCommand;
+ clockOutCommand(writeCommand);
+ switch (dataByte) {
+ case 0xFF:
+ case 0x7F:
+ case 0x80:
+ case 0x00:
+ loopDelay(timeConst.byteWriteDefault);
+ readBack = (unsigned char) clockOutCommand(readCommand);
+ break;
+ default:
+ loopDelay(timeConst.byteWrite);
+ readBack = (unsigned char) clockOutCommand(readCommand);
+ if ((strcmp(device.name, "AT90S1200(A)") != 0 && optimizeFlag != 10)
+ || optimizeFlag == 11) {
+ if (readBack != dataByte) {
+ timeConst.byteWrite += timeConst.byteWriteDefault / 12;
+ timeConst.byteWriteAdjusted = 1;
+ loopDelay(timeConst.byteWrite);
+ readBack = (unsigned char) clockOutCommand(readCommand);
+ } else {
+ if (timeConst.byteWriteAdjusted == 0)
+ timeConst.byteWrite -= timeConst.byteWriteDefault / 12;
+ }
+ }
+ }
+ idx = 1;
+ if (readBack != dataByte) {
+ timeConst.byteWrite = timeConst.byteWriteDefault;
+ timeConst.byteWriteAdjusted = 0;
+ do {
+ clockOutCommand(writeCommand);
+ loopDelay(timeConst.byteWrite);
+ readBack = (unsigned char) clockOutCommand(readCommand);
+ } while (readBack != dataByte && ++idx < 4);
+ writeRetries += idx - 1;
+ if (readBack != dataByte)
+ failure = 1;
+ }
+ return(failure);
+}
+
diff --git a/src/dos_cpt.h b/src/dos_cpt.h
new file mode 100644
index 0000000..8cbfb80
--- /dev/null
+++ b/src/dos_cpt.h
@@ -0,0 +1,53 @@
+/* SP12: A serial programmer for working with Atmel AT90S uCs */
+/* Copyright (C) 1997-2003 Ken Huntington, Kevin Towers, Pitronics. */
+
+/* This program 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. */
+
+/* This program 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 this program; if not, write to the Free Software */
+/* Foundation, Inc., 59 Temple Place - Suite 330, Boston, */
+/* MA 02111-1307, USA. */
+
+/* Pitronics can be reached by email: sbolt@xs4all.nl */
+/* Kevin Towers can be reached by email: ktowers@omnexcontrols.com */
+/* Ken Huntington can be reached by email: kenh@compmore.net */
+
+#ifdef LINUX
+
+#include <sys/io.h>
+#include <unistd.h>
+
+#define inportb(a) inb(a)
+#define outportb(a,b) outb(b,a)
+
+#define delay(a) usleep(a*1000)
+
+#else
+
+#ifdef WIN32
+
+// these functions are in winnt.c
+void win_giveio(void);
+void delay(int mseconds);
+inline unsigned char inportb (const unsigned short port);
+inline void outportb (unsigned short port, const unsigned char val);
+
+#else
+
+#include <dos.h>
+
+#endif // WIN32
+#endif // LINUX
+
+
+
+
+
diff --git a/src/eeprom.c b/src/eeprom.c
new file mode 100644
index 0000000..4e1513a
--- /dev/null
+++ b/src/eeprom.c
@@ -0,0 +1,125 @@
+/* SP12: A serial programmer for working with Atmel AVR uCs */
+/* Copyright (C) 1997-2003 Ken Huntington, Kevin Towers, Pitronics. */
+
+/* This program 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. */
+
+/* This program 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 this program; if not, write to the Free Software */
+/* Foundation, Inc., 59 Temple Place - Suite 330, Boston, */
+/* MA 02111-1307, USA. */
+
+/* Pitronics can be reached by email: sbolt@xs4all.nl */
+/* Kevin Towers can be reached by email: ktowers@omnexcontrols.com */
+/* Ken Huntington can be reached by email: kenh@compmore.net */
+
+#include <stdlib.h>
+#include <stdio.h>
+#include <string.h>
+#include "dos_cpt.h"
+#include "sp12.h"
+
+
+/* Accepts a 16-bit address. Reads the dataByte in two steps */
+/* from the device connected to the Centronics port. */
+/* Power bits prior state assumed on; left on at return. */
+/* SCK prior state assumed lo; left lo at return. */
+
+unsigned char readDataByte(unsigned int address) {
+
+ unsigned int dataByte;
+ unsigned long readCmd;
+
+ readCmd = READ_EEPROM | ((long) address << 8);
+ dataByte = (unsigned char) clockOutCommand(readCmd);
+ return(dataByte);
+}
+
+/* Accepts a dataByte and a 16-bit address. Writes the code in two */
+/* steps into the device connected to the Centronics port and */
+/* verifies arrival. Returns 1 if verify fails three times, else 0. */
+/* The time constant byteWrite is adjusted dynamically, */
+/* if the device is not a 1200(A) and optimizeFlag allows. */
+/* Power bits prior state assumed on; left on at return. */
+/* SCK prior state assumed lo; left lo at return. */
+
+int writeEepromVerified(unsigned int address, unsigned char dataByte,
+ int optimizeFlag) {
+
+ int failure;
+ unsigned long readCmd, writeCmd;
+
+ readCmd = READ_EEPROM | ((long) address << 8);
+ writeCmd = WRITE_EEPROM | ((long) address << 8);
+ writeCmd = writeCmd | dataByte;
+ failure = writeByteVerified(writeCmd, readCmd, optimizeFlag);
+ return(failure);
+}
+
+/* Expects eepromBuf[] to be filled with data. */
+/* executes writeEepromVerified() address by address; */
+/* if verify fails, stops and returns 1, else returns 0. */
+/* Power bits prior state assumed on; left on at return. */
+/* SCK prior state assumed lo; left lo at return. */
+
+int writeEepromArea(unsigned int eepromBuf[], long bufLimit,
+ int optimizeFlag, int overlayFlag) {
+
+ long address, previous;
+
+ previous = 0;
+ printf("...............................................................\r");
+ /*
+ * If the overlayFlag is cleared, all addresses are written to
+ * since all data words (including 0xFF) can be valid.
+ * When the overlayFlag is set, buffer addresses containing
+ * the original 0xFF padding are skipped.
+ */
+ for (address = 0; address < bufLimit; address++) {
+ if (address >= (previous + bufLimit / 64)) {
+ putchar('o');
+ fflush(stdout);
+ previous = address;
+ }
+ if (overlayFlag && eepromBuf[address] > 0xFF)
+ continue;
+ if (writeEepromVerified (address, (unsigned char) eepromBuf[address],
+ optimizeFlag) == 1) {
+ printf("!!\n");
+ return (1);
+ }
+ }
+ printf("\n");
+ return (0);
+}
+
+/* (expects eepromBuf[] to be filled with 0xFF) */
+/* Loops readDataByte into eepromBuf[] */
+/* Power bits prior state assumed on; left on at return. */
+/* SCK prior state assumed lo; left lo at return. */
+
+void readEepromArea(unsigned int eepromBuf[], long bufLimit) {
+
+ long address, previous;
+
+ previous = 0;
+ printf("...............................................................\r");
+
+ for (address = 0; address < bufLimit; address++) {
+ eepromBuf[address] = readDataByte(address);
+ if (address >= (previous + bufLimit / 64)) {
+ putchar('o');
+ fflush(stdout);
+ previous = address;
+ }
+ }
+ printf("\n");
+}
+
diff --git a/src/flash.c b/src/flash.c
new file mode 100644
index 0000000..f70dde1
--- /dev/null
+++ b/src/flash.c
@@ -0,0 +1,292 @@
+/* SP12: A serial programmer for working with Atmel AVR uCs */
+/* Copyright (C) 1997-2008 Ken Huntington, Kevin Towers, */
+/* Artur Pundsack, Pitronics. */
+
+/* This program 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. */
+
+/* This program 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 this program; if not, write to the Free Software */
+/* Foundation, Inc., 59 Temple Place - Suite 330, Boston, */
+/* MA 02111-1307, USA. */
+
+/* Pitronics can be reached by email: sbolt@xs4all.nl */
+/* Kevin Towers can be reached by email: ktowers@omnexcontrols.com */
+/* Ken Huntington can be reached by email: kenh@compmore.net */
+/* Artur Pundsack can be reached by email: ap@pa-tec.de */
+
+#include <stdlib.h>
+#include <stdio.h>
+#include <string.h>
+#include "dos_cpt.h"
+#include "sp12.h"
+
+/* Accepts a 16-bit address. Reads the codeWord in two steps */
+/* from the device connected to the Centronics port. */
+/* Power bits prior state assumed on; left on at return. */
+/* SCK prior state assumed lo; left lo at return. */
+/* AP, 15.01.2008: - The adresswidth has been increased from */
+/* 16 to 32 bits (int -> long), */
+/* - Extended addressing support has been added */
+
+unsigned int readCodeWord(unsigned long address) {
+
+ unsigned char readByte;
+ unsigned int codeWord;
+ unsigned long readCmd;
+ unsigned long writeCmd;
+
+ /* For large ATMegas like ATMega2561 an extended address has to */
+ /* be set when crossing the 64k boundary. */
+ if (address > 0x0000FFFF) {
+ writeCmd = LOAD_EXT_ADDR | ((address & 0x00FF0000) >> 8);
+ clockOutCommand(writeCmd);
+ }
+
+ codeWord = 0;
+ readCmd = READ_PROG_HI | ((address & 0x0000FFFF) << 8);
+ readByte = (unsigned char) clockOutCommand(readCmd);
+ codeWord = codeWord | readByte;
+ readCmd = READ_PROG_LO | ((address & 0x0000FFFF) << 8);
+ readByte = (unsigned char) clockOutCommand(readCmd);
+ codeWord = (codeWord << 8) | readByte;
+ return(codeWord);
+}
+
+/* Accepts a 16-bit codeWord and a 32-bit address. Writes the code */
+/* in two steps with no delays into the ATMega device connected to */
+/* the Centronics port. */
+/* Power bits prior state assumed on; left on at return. */
+/* SCK prior state assumed lo; left lo at return. */
+/* AP, 15.01.2008: - The adresswidth has been increased from */
+/* 16 to 32 bits (int -> long), */
+/* - Extended addressing support has been added */
+void loadCodePage(unsigned long address, unsigned int codeWord) {
+
+ unsigned char loByte;
+ unsigned char hiByte;
+ unsigned long writeCmd;
+
+ loByte = (unsigned char) codeWord & 0x00FF;
+ hiByte = (unsigned char) ((codeWord & 0xFF00) >> 8);
+
+ /* For large ATMegas like ATMega2561 an extended address has to */
+ /* be set once for the first page and when crossing the 64k */
+ /* boundary. */
+ if ((ExtAddr != 1) && (address > 0x0000FFFF)) {
+ writeCmd = LOAD_EXT_ADDR | ((address & 0x00FF0000) >> 8);
+ clockOutCommand(writeCmd);
+ ExtAddr = 1; /* The 64k boundary has been detected */
+ }
+
+ writeCmd = LOAD_PAGE_LO | ((address & 0x000000FF) << 8);
+ writeCmd = writeCmd | loByte;
+ clockOutCommand(writeCmd);
+ writeCmd = LOAD_PAGE_HI | ((address & 0x000000FF) << 8);
+ writeCmd = writeCmd | hiByte;
+ clockOutCommand(writeCmd);
+}
+
+/* Accepts a 16-bit page number and programs it into the ATMega */
+/* device connected to the Centronics parallel port. */
+/* The page is read back and compared with the buffer; if all */
+/* is well, 0 is returned, else 1. */
+/* Power bits prior state assumed on; left on at return. */
+/* SCK prior state assumed lo; left lo at return. */
+
+int writeCodePage(unsigned int flashBuf[], unsigned long address) {
+
+ unsigned long writeCmd;
+ unsigned int idx;
+ unsigned char readByte;
+ unsigned char testByte;
+
+ writeCmd = WRITE_PAGE | ((address & 0x0000FFFF) << 8);
+ clockOutCommand(writeCmd);
+ if (POLL_RDY_BSY) { /* time-out 64ms */
+ testByte = (unsigned char) ~POLL_RDY_BSY;
+ for (idx = 0; idx < 64; idx++) {
+ delay(1);
+ readByte = (unsigned char) clockOutCommand(POLL_RDY_BSY);
+ if ((readByte & testByte) == 0)
+ break;
+ }
+ } else {
+ loopDelay(timeConst.pageWrite);
+ }
+ for (idx = address + 1 - device.pageSize; idx <= address; idx++) {
+ if (flashBuf[idx] != 0xffff) {
+ if (readCodeWord(idx) != flashBuf[idx])
+ return(1);
+ }
+ }
+ return(0);
+}
+
+/* Accepts a 16-bit codeWord and a 16-bit address. Writes the code */
+/* in two steps into the device connected to the Centronics port and */
+/* verifies arrival. Returns 1 if verify fails three times, else 0. */
+/* The time constant byteWrite is adjusted dynamically, */
+/* if the device is not a 1200(A) and optimizeFlag allows. */
+/* Power bits prior state assumed on; left on at return. */
+/* SCK prior state assumed lo; left lo at return. */
+/* AP, 15.01.2008: This function is only available for non page mode!*/
+
+int writeFlashVerified(unsigned int address, unsigned int codeWord,
+ int optimizeFlag) {
+
+ unsigned char loByte;
+ unsigned char hiByte;
+ unsigned long readCmd, writeCmd;
+ int failure;
+
+ loByte = (unsigned char) codeWord & 0x00FF;
+ hiByte = (unsigned char) ((codeWord & 0xFF00) >> 8);
+
+ writeCmd = WRITE_PROG_HI | ((long) address << 8);
+ writeCmd = writeCmd | hiByte;
+ readCmd = READ_PROG_HI | ((long) address << 8);
+ failure = writeByteVerified(writeCmd, readCmd, optimizeFlag);
+ if (failure == 0) {
+ writeCmd = WRITE_PROG_LO | ((long) address << 8);
+ writeCmd = writeCmd | loByte;
+ readCmd = READ_PROG_LO | ((long) address << 8);
+ failure = writeByteVerified(writeCmd, readCmd, optimizeFlag);
+ }
+ return(failure);
+}
+
+/* Expects flashBuf[] to be filled with data. */
+/* If a (page) write fails, 1 is returned, else 0. */
+/* Power bits prior state assumed on; left on at return. */
+/* SCK prior state assumed lo; left lo at return. */
+/* AP, 15.01.2008: - Added reset ext. address marker */
+/* - Set the extended adress byte if necessary */
+
+int writeFlashArea(unsigned int flashBuf[], long bufLimit,
+ int optimizeFlag) {
+
+ long address, previous, writeCmd;
+ int writeFlg = 0;
+ int idx = 1;
+
+ previous = 0;
+ printf("...............................................................\r");
+ if (optimizeFlag != 11)
+ timeConst.pageWriteAdjusted = 1;
+
+ /* AP, 15.01.2008: Added, Reset the extended address marker and */
+ /* set the extended adress byte if neccessary. */
+ ExtAddr = 0;
+ if (device.flashLimit > 0x0000FFFF){
+ writeCmd = LOAD_EXT_ADDR;
+ clockOutCommand(writeCmd);
+ }
+
+ for (address = 0; address < bufLimit; address++) {
+ if (device.pageMode) {
+ /*
+ * To speed things up: Skip locations containing 0xffff
+ * (The device has been erased before writing started)
+ */
+ if (flashBuf[address] != 0xffff) {
+ loadCodePage(address, flashBuf[address]);
+ writeFlg = 1;
+ }
+ if (writeFlg && (address > 0) &&
+ ((address & (device.pageSize - 1)) == device.pageSize - 1)) {
+ writeFlg = 0;
+ if (writeCodePage(flashBuf, address) == 1) {
+ if (++idx < 6) {
+ /*
+ * recalculate first address of the failed page
+ * once only. Note: The calculation below puts
+ * address at the last address value of the previous
+ * page. But address is incremented _after_ this
+ * calculation, _before_ loadCodePage is called
+ * again.
+ */
+ if (idx == 2) address = address - device.pageSize;
+ if (timeConst.pageWriteAdjusted == 1) writeRetries++;
+ } else {
+ printf("!!\n");
+ return (1);
+ }
+ if (optimizeFlag == 11) {
+ timeConst.pageWrite += timeConst.pageWrite / 2;
+ if (timeConst.pageWrite > 1.5 * timeConst.pageWriteDefault) {
+ printf("!!\n");
+ return (1);
+ }
+ timeConst.pageWriteAdjusted = 1;
+ }
+ } else {
+ if (optimizeFlag == 11 && timeConst.pageWriteAdjusted == 0) {
+ timeConst.pageWrite -= timeConst.pageWrite / 2;
+ }
+ idx = 1;
+ }
+ }
+ } else {
+ if (flashBuf[address] != 0xffff) {
+ if (writeFlashVerified((unsigned int) address,
+ flashBuf[address], optimizeFlag) == 1) {
+ printf("!!\n");
+ return (1);
+ }
+ }
+ }
+ if (address >= (previous + bufLimit / 64)) {
+ putchar('o');
+ fflush(stdout);
+ previous = address;
+ }
+ }
+
+ /*
+ * In page mode, a sequence like pageWrite reducing from 64ms
+ * to 32, 16, 8 and 4ms, then adjusting back upwards to 6ms
+ * and finally 9ms is valid; this would cause writeRetries
+ * to be incremented once. Therefore:
+ */
+ if (device.pageMode && writeRetries == 1)
+ writeRetries = 0;
+ printf("\n");
+ return (0);
+}
+
+/* (expects flashBuf[] to be filled with 0xFFFF) */
+/* Loops readCodeWord into flashBuf[] */
+/* Power bits prior state assumed on; left on at return. */
+/* SCK prior state assumed lo; left lo at return. */
+/* AP, 15.01.2008: - Set the extended adress byte if necessary */
+
+void readFlashArea(unsigned int flashBuf[], long bufLimit) {
+
+ long address, previous, writeCmd;
+
+ previous = 0;
+ printf("...............................................................\r");
+
+ if (device.flashLimit > 0x0000FFFF){
+ writeCmd = LOAD_EXT_ADDR;
+ clockOutCommand(writeCmd);
+ }
+
+ for (address = 0; address < bufLimit; address++) {
+ flashBuf[address] = readCodeWord(address);
+ if (address >= (previous + bufLimit / 64)) {
+ putchar('o');
+ fflush(stdout);
+ previous = address;
+ }
+ }
+ printf("\n");
+}
diff --git a/src/init.c b/src/init.c
new file mode 100644
index 0000000..a9798a1
--- /dev/null
+++ b/src/init.c
@@ -0,0 +1,854 @@
+/* SP12: A serial programmer for working with Atmel AVR uCs */
+/* Copyright (C) 1997-2008 Ken Huntington, Kevin Towers, */
+/* Artur Pundsack, Pitronics. */
+
+/* This program 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. */
+
+/* This program 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 this program; if not, write to the Free Software */
+/* Foundation, Inc., 59 Temple Place - Suite 330, Boston, */
+/* MA 02111-1307, USA. */
+
+/* Pitronics can be reached by email: sbolt@xs4all.nl */
+/* Kevin Towers can be reached by email: ktowers@omnexcontrols.com */
+/* Ken Huntington can be reached by email: kenh@compmore.net */
+/* Artur Pundsack can be reached by email: ap@pa-tec.de */
+
+#include <stdlib.h>
+#include <stdio.h>
+#include <string.h>
+#include "dos_cpt.h"
+#include <time.h>
+#include "sp12.h"
+
+
+const char RC_USAGE[] = {
+ "# SP12: A serial programmer for working with Atmel AVR uCs \n"
+ "# Copyright (C) 1997-2008 Ken Huntington, Kevin Towers,\n"
+ "# Artur Pundsack, Pitronics.\n"
+ "# \n"
+ "# This program is free software; you can redistribute it and/or \n"
+ "# modify it under the terms of the GNU General Public License \n"
+ "# as published by the Free Software Foundation; either version 2 \n"
+ "# of the License, or (at your option) any later version. \n"
+ "# \n"
+ "# This program is distributed in the hope that it will be useful, \n"
+ "# but WITHOUT ANY WARRANTY; without even the implied warranty of \n"
+ "# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the \n"
+ "# GNU General Public License for more details.\n"
+ "# \n"
+ "# You should have received a copy of the GNU General Public License \n"
+ "# along with this program; if not, write to the Free Software \n"
+ "# Foundation, Inc., 59 Temple Place - Suite 330, Boston, \n"
+ "# MA 02111-1307, USA. \n"
+ "# \n"
+ "# Pitronics can be reached by email: sbolt@xs4all.nl \n"
+ "# Kevin Towers can be reached by email: ktowers@omnexcontrols.com\n"
+ "# Ken Huntington can be reached by email: kenh@compmore.net \n"
+ "# \n"
+ "# Runtime configuration file to initialise time constants,\n"
+ "# parallel port, logging and port mode (sp12 native or Kanda \n"
+ "# compatible).\n"
+ "# Sp12 will make this file when first started; it will appear \n"
+ "# in the directory as defined by the environment variable SP12,\n"
+ "# or in the current directory if this variable is not set.\n"
+ "# The next time sp12 starts, time constants, port address, logging\n"
+ "# parameters and port mode will be read from this file.\n"
+ "# The primary parallel port address will be automatically selected.\n"
+ "# if you need to use, for instance, the secondary (0x278), then\n"
+ "# by all means edit this file manually, but don't change the *format*\n"
+ "# of the NNNN=ddd type statements, only the values. Note that\n"
+ "# BYTEWRITE is an upper limit; the value actually used is dynamically\n"
+ "# adjusted when sp12 runs. Check changes.doc for other details.\n"};
+
+/*--------------------- The global constants ------------------------*/
+
+unsigned long PROGRAM_ENABLE = 0xAC53FFFFL;
+unsigned long CHIP_ERASE = 0xAC80FFFFL;
+unsigned long READ_DEVICE = 0x30FF00FFL;
+unsigned long READ_PROG_LO = 0x200000FFL;
+unsigned long READ_PROG_HI = 0x280000FFL;
+unsigned long WRITE_PROG_LO = 0x40000000L;
+unsigned long WRITE_PROG_HI = 0x48000000L;
+unsigned long LOAD_PAGE_LO = 0x40000000L;
+unsigned long LOAD_PAGE_HI = 0x48000000L;
+unsigned long LOAD_EXT_ADDR = 0x4D000000L; /* AP, 15.01.2008: Added*/
+unsigned long WRITE_PAGE = 0x4C0000FFL;
+unsigned long READ_EEPROM = 0xA00000FFL;
+unsigned long WRITE_EEPROM = 0xC0000000L;
+unsigned long POLL_RDY_BSY = 0;
+unsigned long WRITE_LOCK_HM = 0;
+unsigned long WRITE_LOCK_LM = 0;
+unsigned long READ_LOCK = 0;
+unsigned long WRITE_FUSES_HM = 0;
+unsigned long WRITE_FUSES_LM = 0;
+unsigned long READ_FUSES = 0;
+unsigned long WRITE_HIGH_FUSES_HM = 0;
+unsigned long WRITE_HIGH_FUSES_LM = 0;
+unsigned long READ_HIGH_FUSES = 0;
+unsigned long WRITE_EXTD_FUSES_HM = 0;
+unsigned long WRITE_EXTD_FUSES_LM = 0;
+unsigned long READ_EXTD_FUSES = 0;
+unsigned long READ_CALIBRATION = 0;
+char CALIB_MESSAGE[MAXLEN] = "";
+char LOCK_MESSAGE[MAXLEN] = "";
+char FUSES_MESSAGE[MAXLEN] = "";
+char H_FUSES_MESSAGE[MAXLEN] = "";
+char X_FUSES_MESSAGE[MAXLEN] = "";
+int POLL_RBLSB = 0;
+int POLL_RBLEN = 0;
+int W_LOCKLSB = 0;
+int R_LOCKLSB = 0;
+int W_LOCKLEN = 0;
+int R_LOCKLEN = 0;
+int W_FUSESLSB = 0;
+int R_FUSESLSB = 0;
+int W_FUSESLEN = 0;
+int R_FUSESLEN = 0;
+int WH_FUSESLSB = 0;
+int RH_FUSESLSB = 0;
+int WH_FUSESLEN = 0;
+int RH_FUSESLEN = 0;
+int WX_FUSESLSB = 0;
+int RX_FUSESLSB = 0;
+int WX_FUSESLEN = 0;
+int RX_FUSESLEN = 0;
+int CALIBLSB = 0;
+int CALIBLEN = 0;
+
+/*------------------- No more global constants ----------------------*/
+
+/*---------------------- The global variables -----------------------*/
+
+unsigned char SCK;
+unsigned char MOSI;
+unsigned char MISO_BITNR;
+unsigned char MISO_INV;
+unsigned char RESET;
+unsigned char ENABLE;
+unsigned char PORTPOWER;
+unsigned char DEFAULT_EXIT_STATE;
+unsigned char PORTACTIVE;
+
+struct timeConsts timeConst;
+struct device_info device;
+struct logging log;
+
+int portAddress;
+int portStatus;
+int writeRetries;
+float clockSpdDefault;
+unsigned char outbyte;
+int KandaMode;
+int ExtAddr; /* AP, 15.01.2008: Added, Marker for crossing the 64k */
+ /* Flash boundary */
+
+/*-------------------- No more global variables ---------------------*/
+
+/* Look for available parallel ports, like the bios does, but */
+/* setting the data register to 0x80 and avoiding some rare */
+/* difficulties. The port address actually set will be the */
+/* last one available in the order 0x278, 0x378, 0x3BC, but */
+/* the status of all three will be reported in the rc file. */
+
+void checkParPort(FILE *rcPtr) {
+
+ printf("Looking for parallel ports...\n");
+
+ outportb(0x278, 0x80); /* Check secondary address, report */
+ /* status in rc file, set portAddress */
+ delay(1); /* Give port time to react */
+ if (inportb(0x278) == 0x80) {
+ portAddress = 0x278;
+ fprintf(rcPtr, "# Parallel port available at 0x278\n");
+ } else {
+ fprintf(rcPtr, "# NO parallel port at 0x278\n");
+ }
+
+ outportb(0x378, 0x80); /* Check primary address, report */
+ /* status in rc file, set portAddress */
+ delay(1); /* Give port time to react */
+ if (inportb(0x378) == 0x80) {
+ portAddress = 0x378;
+ fprintf(rcPtr, "# Parallel port available at 0x378\n");
+ } else {
+ fprintf(rcPtr, "# NO parallel port at 0x378\n");
+ }
+
+ outportb(0x3BC, 0x80); /* Check address on MDPA, report */
+ /* status in rc file, set portAddress */
+ delay(1); /* Give port time to react */
+ if (inportb(0x3BC) == 0x80) {
+ portAddress = 0x3BC;
+ fprintf(rcPtr, "# Parallel port available at 0x3BC\n");
+ } else {
+ fprintf(rcPtr, "# NO parallel port at 0x3BC\n");
+ }
+
+ fprintf(rcPtr, "# This port address will be used:\n");
+ fprintf(rcPtr, "PORT=%#x\n", portAddress);
+}
+
+/* Calibrate several time constants for delays that take care */
+/* of minimum pulse widths. portAddress must be valid. */
+
+void initTimeConst(FILE *rcPtr) {
+
+ clock_t start, end;
+ unsigned long reference;
+ unsigned long Ti; /* Timing loop counter */
+ unsigned long Ni; /* Temp. ref while calibrating */
+ /* timeConst.port */
+
+ printf("Calibrating delay loop. This may take some time...\n");
+
+ /*
+ * Measure time taken by timing loop,
+ * calibrate reference
+ */
+ reference = 1e6; /* 1e6 gives reasonable speed and */
+ /* accuracy in slow PCs like a 286 */
+ do {
+ start = clock();
+ loopDelay(reference);
+ end = clock();
+ /*
+ * Calculate elapsed time in seconds
+ */
+ timeConst.loop = ((double) (end - start)) / CLOCKS_PER_SEC;
+ if (timeConst.loop < 0.005) {
+ reference = reference * 4;
+ continue;
+ }
+ if (timeConst.loop < 1)
+ reference = (unsigned long) (reference * 1.2 / timeConst.loop);
+ } while (timeConst.loop < 1);
+
+ /*
+ * This line alters timeConst.loop as calculated by the loop
+ * above by a factor of two, when compiled by Linux gcc 2.7.
+ * Compiler magic...
+ */
+ timeConst.loop = ((double) (end - start)) / CLOCKS_PER_SEC;
+
+ /*
+ * Measure time taken by outportb() calls
+ */
+ Ni = 1e4;
+ do {
+ start = clock();
+ for (Ti = 0; Ti < Ni; Ti++) {
+ outportb(portAddress, 0x00);
+ }
+ end = clock();
+ /*
+ * Make timeConst.port equal to elapsed time in seconds
+ */
+ timeConst.port = ((double) (end - start)) / CLOCKS_PER_SEC;
+ if (timeConst.port < 0.005) {
+ Ni = Ni * 4;
+ continue;
+ }
+ if (timeConst.port < 1)
+ Ni = (unsigned long) (Ni * 1.2 / timeConst.port);
+ } while (timeConst.port < 1);
+ /*
+ * Set timeConst.port to time taken by 1 outportb() in seconds.
+ */
+ timeConst.port = (timeConst.port - Ni * timeConst.loop / reference) / Ni;
+ /*
+ * Set timeConst.loop to count for 1 second
+ */
+ timeConst.loop = reference / timeConst.loop;
+
+ /* All time constants set to 1 or larger; a check on time constants */
+ /* being zero is later used to estimate rc-file validity */
+
+ /* Calculate pageWrite for 64ms */
+ timeConst.pageWrite = timeConst.loop * 64e-3;
+ if (timeConst.pageWrite == 0)
+ timeConst.pageWrite = 1;
+
+ /* Calculate byteWrite for 12ms */
+ timeConst.byteWrite = timeConst.loop * 12e-3;
+ if (timeConst.byteWrite == 0)
+ timeConst.byteWrite = 1;
+
+ /* Calculate resetPuls for 50ms */
+ timeConst.resetPuls = timeConst.loop * 50e-3;
+ if (timeConst.resetPuls == 0)
+ timeConst.resetPuls = 1;
+
+ /* Set the other time constants in milliseconds */
+ timeConst.programEnable = 40;
+ timeConst.chipErase = 120; /* assume ATMega worst case */
+ timeConst.powerOn = 200;
+
+ /* Set the default uC clock speed in MHz */
+ clockSpdDefault = 0.7;
+
+ fprintf(rcPtr, "# Set KANDA=1 to adapt the parallel port pinout\n");
+ fprintf(rcPtr, "# to the cable/dongles supplied with the\n");
+ fprintf(rcPtr, "# Atmel/Kanda STK200/300 starter kits.\n");
+ fprintf(rcPtr, "# When KANDA=0 (default) the pinout conforms\n");
+ fprintf(rcPtr, "# to the original SP12 cable and Ken Huntington's\n");
+ fprintf(rcPtr, "# dongle.\n");
+ fprintf(rcPtr, "# If KANDA is removed or set to 3, sp12 will behave\n");
+ fprintf(rcPtr, "# according to the name by which it is called,\n");
+ fprintf(rcPtr, "# 'sp12' or 'sp12.kanda'.\n");
+ fprintf(rcPtr, "KANDA=%d\n", KandaMode);
+ fprintf(rcPtr, "# Set the log path/file to log writes and locks.\n");
+ fprintf(rcPtr, "# (less than 200 characters)\n");
+ fprintf(rcPtr, "LOGPATH=%s\n", log.logPath);
+ fprintf(rcPtr, "# Set logging=1 to activate logging:\n");
+ fprintf(rcPtr, "LOGGING=%d\n", log.logging);
+ fprintf(rcPtr, "# Set both logging and query=1 to always query writes:\n");
+ fprintf(rcPtr, "QUERY=%d\n", log.query);
+ fprintf(rcPtr, "# Chip erase delay, 120ms ATMega, 20ms others:\n");
+ fprintf(rcPtr, "CHIPERASE=%d\n", timeConst.chipErase);
+ fprintf(rcPtr, "# Time constant for 200ms will be:\n");
+ fprintf(rcPtr, "POWERON=%d\n", timeConst.powerOn);
+ fprintf(rcPtr, "# Set clockSpdDefault to a suitable value in MHz.\n");
+ fprintf(rcPtr, "CLOCKSPDDEFAULT=%.1f\n", clockSpdDefault);
+ fprintf(rcPtr, "# loopCount is a calibrated number which\n");
+ fprintf(rcPtr, "# represents about 1 second, and used to\n");
+ fprintf(rcPtr, "# calculate time constants as demanded by\n");
+ fprintf(rcPtr, "# clockSpdDefault and the -Mn.n clock speed\n");
+ fprintf(rcPtr, "# setting.\n");
+ fprintf(rcPtr, "LOOPCOUNT=%g\n", timeConst.loop);
+ fprintf(rcPtr, "# Do not change anything below this line.\n");
+ fprintf(rcPtr, "# ---------------------------------------\n");
+ fprintf(rcPtr, "PORTACCESSTIME=%g\n", timeConst.port);
+ fprintf(rcPtr, "BYTEWRITE=%ld\n", timeConst.byteWrite);
+ fprintf(rcPtr, "PAGEWRITE=%ld\n", timeConst.pageWrite);
+ fprintf(rcPtr, "PROGRAMENABLE=%d\n", timeConst.programEnable);
+ fprintf(rcPtr, "RESETPULS=%ld\n", timeConst.resetPuls);
+}
+
+/* Look for the rc file, using environment variable `SP12' (if */
+/* this variable has not been set, the current directory is used). */
+/* If the rc file exists, then read the time constants and the */
+/* parallel port address from this file. If the rc file is not */
+/* found, then calibrate the time constants, check for available */
+/* parallel ports, set (usually the primary) address, and write */
+/* it all into a new rc file. Afterwards check if the parallel */
+/* port is actually there (guarding against a corrupt rc file, and */
+/* setting the port data register to 0x80), and also check if the */
+/* time constants aren't 0. exit(1) if a check fails, else set */
+/* parallel port data register 0x00, and portStatus to */
+/* portAddress + 1 */
+/* Based on either the KANDA setting in _sp12rc or the name by */
+/* which the program is called (sp12 or sp12.kanda), the */
+/* parallel port pinout is adapted to the cable/dongles supplied */
+/* with the Atmel/Kanda STK200/300 starter kits, or to the */
+/* original SP12 cable and Ken Huntington's dongle. */
+
+void initSp12(char *howWeAreCalled) {
+
+ FILE *rcPtr;
+ char *sp12rc;
+ char rcPath[MAXLEN];
+ char rcLine[MAXLEN];
+
+ timeConst.sckLO = 0;
+ timeConst.sckHI = 0;
+ timeConst.resetPuls = 0;
+ timeConst.programEnable = 0;
+ timeConst.chipErase = 0;
+ timeConst.byteWrite = 0;
+ timeConst.powerOn = 0;
+
+ portAddress = 0;
+ portStatus = 0;
+ writeRetries = 0;
+ timeConst.byteWriteAdjusted = 0;
+ timeConst.pageWriteAdjusted = 0;
+ log.logging = 0;
+ log.query = 0;
+ strcpy(log.logPath, "sp12log.txt");
+ KandaMode = 3; /* if it remains 3 after reading _sp12rc, */
+ /* check how we were called */
+
+ if ((sp12rc = getenv("SP12")) != NULL) {
+ strcpy(rcPath, sp12rc);
+#ifdef LINUX
+ if (rcPath[strlen(rcPath)-1] != '/')
+ strcat (rcPath, "/");
+#else
+ if (rcPath[strlen(rcPath)-1] != '\\')
+ strcat (rcPath, "\\");
+#endif
+ printf("Path to _sp12rc and _sp12dev: %s\n", rcPath);
+ strcat(rcPath, "_sp12rc");
+ } else {
+ strcpy(rcPath, "_sp12rc");
+ printf("Path to _sp12rc and _sp12dev: Local directory\n");
+ }
+ if ((rcPtr = fopen(rcPath, "r")) != NULL) {
+ /*
+ * Read data from existing rc file, or...
+ */
+ while (fgets(rcLine, MAXLEN, rcPtr) != NULL) {
+ sscanf(rcLine, "PORT=%x", &portAddress);
+ sscanf(rcLine, "KANDA=%d", &KandaMode);
+ sscanf(rcLine, "LOGPATH=%s", log.logPath);
+ sscanf(rcLine, "LOGGING=%d", &log.logging);
+ sscanf(rcLine, "QUERY=%d", &log.query);
+ sscanf(rcLine, "RESETPULS=%ld", &timeConst.resetPuls);
+ sscanf(rcLine, "CHIPERASE=%d", &timeConst.chipErase);
+ sscanf(rcLine, "POWERON=%d", &timeConst.powerOn);
+ sscanf(rcLine, "CLOCKSPDDEFAULT=%f", &clockSpdDefault);
+ sscanf(rcLine, "LOOPCOUNT=%lg", &timeConst.loop);
+ sscanf(rcLine, "PORTACCESSTIME=%lg", &timeConst.port);
+ sscanf(rcLine, "BYTEWRITE=%ld", &timeConst.byteWrite);
+ sscanf(rcLine, "PAGEWRITE=%ld", &timeConst.pageWrite);
+ sscanf(rcLine, "PROGRAMENABLE=%d", &timeConst.programEnable);
+ }
+ } else {
+ /*
+ * Make a new rc file
+ */
+ delay(100); /* helps calibration to reach same values every time */
+ if ((rcPtr = fopen(rcPath, "w")) == NULL) {
+ fprintf(stderr, "Unable to open %s for writing.\n", rcPath);
+ exit(1);
+ }
+ if (strrchr(howWeAreCalled, '/') != NULL)
+ howWeAreCalled = (strrchr(howWeAreCalled, '/') + 1);
+ if (strrchr(howWeAreCalled, '\\') != NULL)
+ howWeAreCalled = (strrchr(howWeAreCalled, '\\') + 1);
+ if (strchr(howWeAreCalled, 'k') == NULL) {
+ KandaMode = 0; /* default is sp12 cable/dongle */
+ } else {
+ KandaMode = 1;
+ }
+ fprintf(rcPtr, "%s\n", RC_USAGE);
+ checkParPort(rcPtr);
+ if (portAddress) /* port must be valid, else segfault */
+ initTimeConst(rcPtr);
+ }
+ fclose(rcPtr);
+ if (!portAddress) /* no port found -> remove broken _sp12rc */
+ remove(rcPath);
+
+ /*
+ * Do check if the time constants aren't zero...
+ */
+ if (portAddress == 0 || timeConst.loop == 0 || timeConst.port == 0
+ || timeConst.programEnable == 0 || timeConst.chipErase == 0
+ || timeConst.byteWrite == 0 || timeConst.powerOn == 0
+ || timeConst.pageWrite == 0) {
+ fprintf(stderr, "Initialisation has failed (parallel port not found,\n");
+ fprintf(stderr, "or %s corrupt).\n", rcPath);
+ exit(1);
+ }
+ timeConst.byteWriteDefault = timeConst.byteWrite;
+ timeConst.pageWriteDefault = timeConst.pageWrite;
+ /*
+ * And if the port is actually there.
+ */
+ outportb(portAddress, 0x80);
+ if (inportb(portAddress) != 0x80) {
+ fprintf(stderr, "Initialisation has failed (parallel port not\n");
+ fprintf(stderr, "responding, or %s corrupt).\n", rcPath);
+ exit(1);
+ } else {
+ outportb(portAddress, 0x00);
+ portStatus = portAddress + 1;
+ }
+ /*
+ * Now set the parallel port pinout to match
+ * either the original sp12 cable and Ken's `dongle'
+ * or the cable/dongle as supplied with the
+ * Atmel/Kanda STK200/300 starter kits.
+ */
+ if (strrchr(howWeAreCalled, '/') != NULL)
+ howWeAreCalled = (strrchr(howWeAreCalled, '/') + 1);
+ if (strrchr(howWeAreCalled, '\\') != NULL)
+ howWeAreCalled = (strrchr(howWeAreCalled, '\\') + 1);
+ if (KandaMode == 0 || (KandaMode == 3 && strchr(howWeAreCalled, 'k') == NULL)) {
+ SCK = S_SCK;
+ MOSI = S_MOSI;
+ MISO_BITNR = S_MISO_BITNR;
+ MISO_INV = S_MISO_INV;
+ RESET = S_RESET;
+ ENABLE = S_ENABLE;
+ PORTPOWER = S_PORTPOWER;
+ DEFAULT_EXIT_STATE = S_DEFAULT_EXIT_STATE;
+ PORTACTIVE = S_PORTACTIVE;
+ printf("Running in SP12 cable/dongle compatible mode.\n");
+ } else if (KandaMode == 1 || \
+ (KandaMode == 3 && strchr(howWeAreCalled, 'k') != NULL)) {
+ SCK = K_SCK;
+ MOSI = K_MOSI;
+ MISO_BITNR = K_MISO_BITNR;
+ MISO_INV = K_MISO_INV;
+ RESET = K_RESET;
+ ENABLE = K_ENABLE;
+ PORTPOWER = K_PORTPOWER;
+ DEFAULT_EXIT_STATE = K_DEFAULT_EXIT_STATE;
+ PORTACTIVE = K_PORTACTIVE;
+ printf("Running in Kanda cable/dongle compatible mode.\n");
+ } else {
+ fprintf(stderr, "Call me 'sp12' or 'sp12.kanda', or set KANDA=0 or KANDA=1\n");
+ exit(1);
+ }
+ outbyte = PORTPOWER; /* Default: start with `port power' on */
+ /* (all zeroes when KANDA=1) */
+}
+
+
+/* Extract a lock or fuse command (read, write) from a rawLine */
+/* scanned from _sp12dev. Determine the lsb position and length */
+/* of the lock or fuses bit block, which is marked as something */
+/* like '6543' or 'iiiiii' or 'ooooooo' */
+
+int extractFuseCmnd(char *rawLine, unsigned long *commandHM, \
+ unsigned long *commandLM, int *fusesLsb, int *fusesLen) {
+
+ int idx = 0;
+ int idy = 0;
+ int chr = 0;
+ int fusesMsb = 0;
+ int firstBit = 0;
+ char procLine[MAXLEN];
+ char hiMask[MAXLEN] = "";
+ char loMask[MAXLEN] = "";
+
+ /*
+ * Command must be eight nibbles long, separated by spaces,
+ * containing only the characters we scanned for;
+ * but removing trailing spaces doesn't hurt.
+ */
+ for (idx = strlen(rawLine) - 1; idx > 0; idx--) {
+ if (rawLine[idx] != ' ')
+ break;
+ else
+ rawLine[idx] = '\0';
+ }
+// printf("len %i, raw %s\n", strlen(rawLine), rawLine);
+ if (strlen(rawLine) != 39)
+ return(1);
+ /*
+ * Remove the spaces
+ */
+ idy = 0;
+ for (idx = 0; idx < strlen(rawLine); idx++) {
+ if (rawLine[idx] != ' ') {
+ procLine[idy++] = rawLine[idx];
+ procLine[idy] = '\0';
+ }
+ }
+ /*
+ * Convert the characters into ones and zeroes,
+ * determine the position of the fuse lsb
+ */
+ firstBit = 0;
+ for (idx = 0; idx < strlen(procLine); idx++) {
+ chr = procLine[idx];
+ if (chr == 'h' || chr == 'x') {
+ hiMask[idx] = '1';
+ loMask[idx] = '1';
+ } else if (chr == 'l') {
+ hiMask[idx] = '0';
+ loMask[idx] = '0';
+ } else {
+ if (!firstBit) {
+ firstBit = 1;
+ fusesMsb = 31 - idx;
+ }
+ *fusesLsb = 31 - idx;
+ hiMask[idx] = '1';
+ loMask[idx] = '0';
+ }
+// printf("hiMask %s\n", hiMask);
+// printf("loMask %s\n", loMask);
+ }
+ *fusesLen = fusesMsb - *fusesLsb + 1;
+ *commandHM = strtoul(hiMask, NULL, 2);
+ *commandLM = strtoul(loMask, NULL, 2);
+ return(0);
+}
+
+
+/* Takes a devicecode (like 0392) or identifier ((-i)433) and sets */
+/* the device commands, messages and other parameters as found in */
+/* _sp12dev. */
+/* Returns 1 if _sp12dev not found; 2 if a line in _sp12dev is */
+/* corrupt; 3 if _sp12dev has no entry for the deviceCode or */
+/* identifier and the deviceCode (as an int) if all is well. */
+
+int setDevicePars(int initFlag, char *identifier) {
+
+ FILE *rcPtr;
+ char rcPath[MAXLEN];
+ char *sp12dev;
+ int deviceCode = 0;
+ char rcLine[MAXLEN] = "";
+ char rawLine[MAXLEN] = "";
+ char match[MAXLEN] = "";
+ int begin = 0; /* high when device code matches */
+ int found = 0; /* high when device was identified */
+ int iNum = 0;
+ int iFlag = 0; /* high inside ident ('begin') block */
+ int idx = 0;
+ int idy = 0;
+ int idz = 0;
+ unsigned long dummyHi = 0;
+
+
+ if ((sp12dev = getenv("SP12")) != NULL) {
+ strcpy(rcPath, sp12dev);
+#ifdef LINUX
+ if (rcPath[strlen(rcPath)-1] != '/')
+ strcat (rcPath, "/");
+#else
+ if (rcPath[strlen(rcPath)-1] != '\\')
+ strcat (rcPath, "\\");
+#endif
+ strcat(rcPath, "_sp12dev");
+ } else {
+ strcpy(rcPath, "_sp12dev");
+ }
+
+ if ((rcPtr = fopen(rcPath, "r")) == NULL) {
+ return(1);
+ }
+ /*
+ * Fetch commands
+ */
+ readDeviceCode();
+ deviceCode = deviceCode | device.sigByte_2;
+ deviceCode = deviceCode << 8;
+ deviceCode = deviceCode | device.sigByte_1;
+ switch (device.sigByte_0) {
+ case 0x1E:
+ strcpy(device.madeBy, "Atmel");
+ break;
+ default:
+ strcpy(device.madeBy, "an unknown manufacturer");
+ }
+ begin = 0;
+ iFlag = 0;
+ found = 0;
+ strcpy(LOCK_MESSAGE, "");
+ strcpy(FUSES_MESSAGE, "");
+ strcpy(device.name, "unknown device, or no device");
+ device.flashLimit = 0;
+ device.eepromLimit = 0;
+ device.pageMode = 0;
+ device.pageSize = 0;
+ while (fgets(rcLine, MAXLEN, rcPtr) != NULL) {
+ if (sscanf(rcLine, "begin %[0-9A-Fa-f]", rawLine)) {
+ if (!initFlag && strtol(rawLine, NULL, 16) == deviceCode) {
+ begin = 1;
+ found = 1;
+ } else if (!iFlag) {
+ begin = 0;
+ }
+ iFlag = 1;
+ } else if (sscanf(rcLine, "-i%[0-9a-zA-Z]", rawLine)) {
+ /*
+ * All defined characters must be there, in the right order
+ */
+ idy = 0;
+ idz = 0;
+ for (idx = 0; idx < strlen(identifier); idx++) {
+ if (identifier[idx] > 90) /* reduce a-z to A-Z */
+ identifier[idx] = identifier[idx] - 32;
+ if (identifier[idx] == rawLine[idy]) {
+ match[idz++] = identifier[idx];
+ if (++idy >= strlen(rawLine))
+ break;
+ } else if (idz && identifier[idx] < 58 && identifier[idx] > 47) {
+ break;
+ }
+ }
+ if (strcmp(match, rawLine) == 0) {
+ begin = 1;
+ found++;
+ } else if (iFlag && initFlag) {
+ begin = 0;
+ }
+ iFlag = 0;
+ }
+ if(found > 1) {
+ fprintf(stderr, "More than one ident: ");
+ return(2);
+ }
+
+ if (begin && sscanf(rcLine, "DEVICENAME = %[A-Z ()0-9a-z]", rawLine) == 1) {
+ strcpy(device.name, rawLine);
+ }
+ if (begin && sscanf(rcLine, "FLASHSIZE = %i", &iNum) == 1) {
+ device.flashLimit = iNum;
+ }
+ if (begin && sscanf(rcLine, "EEPROMSIZE = %i", &iNum) == 1) {
+ device.eepromLimit = iNum;
+ }
+ if (begin && sscanf(rcLine, "PAGEMODE = %i", &iNum) == 1) {
+ device.pageMode = iNum;
+ }
+ if (begin && sscanf(rcLine, "PAGESIZE = %i", &iNum) == 1) {
+ device.pageSize = iNum;
+ }
+ if (begin && sscanf(rcLine, "POLL_RDY_BSY = %[hlxo 0-9]", rawLine) == 1) {
+ if (extractFuseCmnd(rawLine, &dummyHi, &POLL_RDY_BSY, \
+ &POLL_RBLSB, &POLL_RBLEN)) {
+ fprintf(stderr, "_sp12dev line 'POLL_RDY_BSY' corrupt\n");
+ return(2);
+ }
+ }
+ if (begin && sscanf(rcLine, "WRITE_LOCK = %[hlxi 0-9]", rawLine) == 1) {
+ if (extractFuseCmnd(rawLine, &WRITE_LOCK_HM, &WRITE_LOCK_LM, \
+ &W_LOCKLSB, &W_LOCKLEN)) {
+ fprintf(stderr, "_sp12dev line 'WRITE_LOCK' corrupt\n");
+ return(2);
+ }
+ }
+ if (begin && sscanf(rcLine, "READ_LOCK = %[hlxo 0-9]", rawLine) == 1) {
+ if (extractFuseCmnd(rawLine, &dummyHi, &READ_LOCK, &R_LOCKLSB, \
+ &R_LOCKLEN)) {
+ fprintf(stderr, "_sp12dev line 'READ_LOCK' corrupt\n");
+ return(2);
+ }
+ }
+ if (begin && sscanf(rcLine, "LOCK_MESSAGE = %[^][]", rawLine) == 1) {
+ strncat(LOCK_MESSAGE, rawLine, MAXLEN - strlen(LOCK_MESSAGE) - 2);
+ LOCK_MESSAGE[MAXLEN - 1] = '\0';
+ }
+ if (begin && sscanf(rcLine, "WRITE_FUSES = %[hlxi 0-9A-Z]", rawLine) == 1) {
+ if (extractFuseCmnd(rawLine, &WRITE_FUSES_HM, &WRITE_FUSES_LM,
+ &W_FUSESLSB, &W_FUSESLEN)) {
+ fprintf(stderr, "_sp12dev line 'WRITE_FUSES' corrupt\n");
+ return(2);
+ }
+ }
+ if (begin && sscanf(rcLine, "READ_FUSES = %[hlxo 0-9A-Z]", rawLine) == 1) {
+ if (extractFuseCmnd(rawLine, &dummyHi, &READ_FUSES, &R_FUSESLSB, \
+ &R_FUSESLEN)) {
+ fprintf(stderr, "_sp12dev line 'READ_FUSES' corrupt\n");
+ return(2);
+ }
+ }
+ if (begin && sscanf(rcLine, "FUSES_MESSAGE = %[^][]", rawLine) == 1) {
+ strncat(FUSES_MESSAGE, rawLine, MAXLEN - strlen(FUSES_MESSAGE) - 2);
+ FUSES_MESSAGE[MAXLEN - 1] = '\0';
+ }
+ if (begin && sscanf(rcLine, "WRITE_HIGH_FUSES = %[hlxi 0-9A-Z]", rawLine) == 1) {
+ if (extractFuseCmnd(rawLine, &WRITE_HIGH_FUSES_HM, &WRITE_HIGH_FUSES_LM,
+ &WH_FUSESLSB, &WH_FUSESLEN)) {
+ fprintf(stderr, "_sp12dev line 'WRITE_HIGH_FUSES' corrupt\n");
+ return(2);
+ }
+ }
+ if (begin && sscanf(rcLine, "READ_HIGH_FUSES = %[hlxo 0-9A-Z]", rawLine) == 1) {
+ if (extractFuseCmnd(rawLine, &dummyHi, &READ_HIGH_FUSES, &RH_FUSESLSB,
+ &RH_FUSESLEN)) {
+ fprintf(stderr, "_sp12dev line 'READ_HIGH_FUSES' corrupt\n");
+ return(2);
+ }
+ }
+ if (begin && sscanf(rcLine, "HIGH_FUSES_MESSAGE = %[^][]", rawLine) == 1) {
+ strncat(H_FUSES_MESSAGE, rawLine, MAXLEN - strlen(H_FUSES_MESSAGE) - 2);
+ H_FUSES_MESSAGE[MAXLEN - 1] = '\0';
+ }
+ if (begin && sscanf(rcLine, "WRITE_EXTD_FUSES = %[hlxi 0-9A-Z]", rawLine) == 1) {
+ if (extractFuseCmnd(rawLine, &WRITE_EXTD_FUSES_HM, &WRITE_EXTD_FUSES_LM,
+ &WX_FUSESLSB, &WX_FUSESLEN)) {
+ fprintf(stderr, "_sp12dev line 'WRITE_EXTD_FUSES' corrupt\n");
+ return(2);
+ }
+ }
+ if (begin && sscanf(rcLine, "READ_EXTD_FUSES = %[hlxo 0-9A-Z]", rawLine) == 1) {
+ if (extractFuseCmnd(rawLine, &dummyHi, &READ_EXTD_FUSES, &RX_FUSESLSB,
+ &RX_FUSESLEN)) {
+ fprintf(stderr, "_sp12dev line 'READ_EXTD_FUSES' corrupt\n");
+ return(2);
+ }
+ }
+ if (begin && sscanf(rcLine, "EXTD_FUSES_MESSAGE = %[^][]", rawLine) == 1) {
+ strncat(X_FUSES_MESSAGE, rawLine, MAXLEN - strlen(X_FUSES_MESSAGE) - 2);
+ X_FUSES_MESSAGE[MAXLEN - 1] = '\0';
+ }
+ if (begin && sscanf(rcLine, "READ_CALIBRATION = %[hlxo 0-9]", rawLine) == 1) {
+ if (extractFuseCmnd(rawLine, &dummyHi, &READ_CALIBRATION, &CALIBLSB,
+ &CALIBLEN)) {
+ fprintf(stderr, "_sp12dev line 'READ_CALIBRATION' corrupt\n");
+ return(2);
+ }
+ }
+ if (begin && sscanf(rcLine, "CALIB_MESSAGE = %[^][]", rawLine) == 1) {
+ strncat(CALIB_MESSAGE, rawLine, MAXLEN - strlen(CALIB_MESSAGE) - 2);
+ CALIB_MESSAGE[MAXLEN - 1] = '\0';
+ }
+ }
+ fclose(rcPtr);
+ if (!found)
+ return(3);
+ device.initPerformed = 1;
+ return(deviceCode);
+}
+
+/* Collects all device codes and names from _sp12dev, and shows */
+/* them on screen. */
+/* Returns 1 if _sp12dev not found, else zero. */
+
+int supported(void) {
+
+ FILE *rcPtr;
+ char rcPath[MAXLEN];
+ char *sp12dev;
+ char rcLine[MAXLEN];
+ char rawLine[MAXLEN];
+ int begin = 0;
+ int found = 0;
+
+ if ((sp12dev = getenv("SP12")) != NULL) {
+ strcpy(rcPath, sp12dev);
+#ifdef LINUX
+ if (rcPath[strlen(rcPath)-1] != '/')
+ strcat (rcPath, "/");
+#else
+ if (rcPath[strlen(rcPath)-1] != '\\')
+ strcat (rcPath, "\\");
+#endif
+ strcat(rcPath, "_sp12dev");
+ } else {
+ strcpy(rcPath, "_sp12dev");
+ }
+
+ if ((rcPtr = fopen(rcPath, "r")) == NULL) {
+ return(1);
+ }
+ /*
+ * Fetch and show codes and names
+ */
+ begin = 0;
+ printf("Supported devices:\n");
+ while (fgets(rcLine, MAXLEN, rcPtr) != NULL) {
+ if (sscanf(rcLine, "begin %[0-9A-Fa-f]", rawLine)) {
+ printf("%s, ", rawLine);
+ begin = 1;
+ found = 1;
+ }
+ if (begin && sscanf(rcLine, "DEVICENAME = %[A-Z ()0-9a-z]", rawLine) == 1) {
+ begin = 0;
+ printf("%s\n", rawLine);
+ }
+ }
+ fclose(rcPtr);
+ if (!found)
+ printf("No devices found.\n");
+ else
+ printf("\n");
+ return(0);
+}
diff --git a/src/makefile b/src/makefile
new file mode 100644
index 0000000..cc01f38
--- /dev/null
+++ b/src/makefile
@@ -0,0 +1,58 @@
+#
+# Makefile for the Free Software Foundations gcc compiler
+# Optimize flag = 1 to achieve correct timing
+#
+CC=gcc
+LINK=gcc
+#
+# LINUX FLAGS
+CFLAGS=-c -O1 -Wall -DLINUX
+LFLAGS=-s -o
+TARGET=sp12
+ERASE=rm -f
+OBJECTS=sp12.o init.o flash.o device.o buffer.o eeprom.o
+
+# DOS FLAGS
+#CFLAGS=-c -O1 -Wall
+#LFLAGS=-lemu -s -o
+#TARGET=sp12.exe
+#ERASE=del
+#OBJECTS=sp12.o init.o flash.o device.o buffer.o eeprom.o
+
+# Win FLAGS - MingW
+#CFLAGS=-c -O1 -Wall -mwindows -mconsole
+#LFLAGS=-s -o
+#TARGET=sp12.exe
+#ERASE=del
+#OBJECTS=sp12.o init.o flash.o device.o buffer.o eeprom.o winnt.o
+
+all: $(TARGET)
+
+sp12.o: sp12.c
+ $(CC) $(CFLAGS) sp12.c
+
+init.o: init.c
+ $(CC) $(CFLAGS) init.c
+
+flash.o: flash.c
+ $(CC) $(CFLAGS) flash.c
+
+device.o: device.c
+ $(CC) $(CFLAGS) device.c
+
+buffer.o: buffer.c
+ $(CC) $(CFLAGS) buffer.c
+
+eeprom.o: eeprom.c
+ $(CC) $(CFLAGS) eeprom.c
+
+winnt.o: winnt.c
+ $(CC) $(CFLAGS) winnt.c
+
+$(TARGET): makefile $(OBJECTS)
+ $(LINK) $(LFLAGS) $(TARGET) $(OBJECTS)
+
+clean:
+ $(ERASE) $(TARGET)
+ $(ERASE) *.o
+
diff --git a/src/sp12.c b/src/sp12.c
new file mode 100644
index 0000000..738f404
--- /dev/null
+++ b/src/sp12.c
@@ -0,0 +1,1136 @@
+/* SP12: A serial programmer for working with Atmel AVR uCs */
+/* Copyright (C) 1997-2008 Ken Huntington, Kevin Towers, */
+/* Artur Pundsack, Pitronics. */
+
+/* This program 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. */
+
+/* This program 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 this program; if not, write to the Free Software */
+/* Foundation, Inc., 59 Temple Place - Suite 330, Boston, */
+/* MA 02111-1307, USA. */
+
+/* Pitronics can be reached by email: sbolt@xs4all.nl */
+/* Kevin Towers can be reached by email: ktowers@omnexcontrols.com */
+/* Ken Huntington can be reached by email: kenh@compmore.net */
+/* Artur Pundsack can be reached by email: ap@pa-tec.de */
+
+#include <stdlib.h>
+#include <stdio.h>
+#include <string.h>
+#include "dos_cpt.h"
+#include <time.h>
+#include "sp12.h"
+
+#define VERSION "2.1.1"
+
+
+const char USAGE[] = {
+ " SP12/32 version %s: A serial programmer for Atmel AVR uCs.\n"
+ " Copyright (C) 1997-2008 Ken Huntington, Kevin Towers,\n"
+ " Artur Pundsack, Pitronics.\n"
+ "SYNOPSIS\n"
+ " sp12 {-options} [filename or address(:data)] {-options} [filename...\n"
+ "OPTIONS\n"
+ " i[nnn] - init E - chip erase\n"
+ " e - eprom area B - blank check\n"
+ " p - program area C - calculate checksum\n"
+ " w - write to L[n...n] - lock fuses\n"
+ " r - read from F[n...n] - fuses\n"
+ " a - address(:data) next H[n...n] - high fuses\n"
+ " f - filename next X[n...n] - extended fuses\n"
+ " I - create Intel Hex file c[nn] - calibration byte(s)\n"
+ " h - hex dump with ascii M<n.n> - match Sck to uC clock\n"
+ " o0, o1 - optimization P<nnn> - parallel port control\n"
+ " s - show supported uCs t - timing check\n"
+ "EXAMPLES\n"
+ " sp12 -M4 -wF10011 -wpfC prog.hex -wefC eep.hex -wL00 (sets timing \n"
+ " for 4MHz, sets fuses, then writes prog.hex into the program area\n"
+ " and eep.hex into the eeprom, providing checksums for both; and\n"
+ " finally a read/write lock is set)\n"
+ " sp12 -rF -rH -rL -rc (reads fuses, locks and calibration if possible)\n"
+ " sp12 -wea 0x01:0x99 (Writes 0x99 into eeprom address 0x01)\n\n"};
+
+static int initFlag = 0; /* i<nn> on command line; flag set nn */
+static int flashFlag = 0; /* p on command line */
+static int eepromFlag = 0; /* e */
+static int writeFlag = 0; /* w */
+static int readFlag = 0; /* r */
+static int lockFlag = 0; /* L */
+static int fusesFlag = 0; /* F */
+static int highFFlag = 0; /* H */
+static int extdFFlag = 0; /* X */
+static int calibFlag = 0; /* c */
+static unsigned long calibAddress = 0;
+static int fileFlag = 0; /* d */
+static int addressFlag = 0; /* a */
+static int eraseFlag = 0; /* E */
+static int blankChkFlag = 0; /* B */
+static int checksumFlag = 0; /* C */
+static int testFlag = 0; /* T */
+static int portFlag = 0; /* P */
+static int optimizeFlag = 0; /* o, 0, 1 */
+static int overlayFlag = 0; /* O */
+static float clkSpeed = 0; /* M, value between 0.1 and 12 */
+static float prevClkSpeed = 0; /* previous clkSpeed */
+static int IntelFlag = 0; /* I */
+static int hexAsciiFlag = 0; /* h */
+static int addressAvailable = 0;
+static int pathAvailable = 0;
+static char flashPath[MAXLEN]; /* path/filename source or destination */
+static char eepromPath[MAXLEN]; /* path/filename source or destination */
+static char checksumPath[MAXLEN]; /* path/filename source */
+static char commandStr[MAXLEN]; /* stores commands for logging */
+static char fusesBits[MAXLEN] = "";
+static char lockBits[MAXLEN] = "";
+static char calibByte[MAXLEN] = "";
+static char identifier[MAXLEN] = "";
+static unsigned int flashAddress = 0;
+static unsigned int flashData = 0;
+static unsigned int eepromAddress = 0;
+static unsigned int eepromData = 0;
+static unsigned int *flashBuf; /* flash buffer */
+static unsigned int *eepromBuf; /* eeprom buffer */
+static clock_t sp12Start, sp12End;
+static double sp12Elapsed;
+
+
+void exitSp12(int exitState) {
+
+ char binary[9] = "76543210"; /* Stores conversion num to binary */
+
+ portControl(DEFAULT_EXIT_STATE, 1);
+ num2bin((unsigned char) DEFAULT_EXIT_STATE, binary);
+ printf("Writing %#04x (%d, B%s) ",
+ DEFAULT_EXIT_STATE, DEFAULT_EXIT_STATE, binary);
+ printf("to the parallel port data bits.\n");
+ sp12End = clock();
+ sp12Elapsed = ((double) (sp12End - sp12Start)) / CLOCKS_PER_SEC;
+ printf("Sp12 was active for %#3.2f seconds.\n", sp12Elapsed);
+ if (flashBuf)
+ free(flashBuf);
+ if (eepromBuf)
+ free(eepromBuf);
+ exit(exitState);
+}
+
+
+/* Merely check whether options -L, -E, -F, -R, -S, -K, D, V, */
+/* A or U was used, and say something about it. */
+
+void reportLEFHXcFlag(void) {
+
+ if (lockFlag) {
+ printf("You have selected option -L to read or write lock fuses,\n");
+ printf("which has had no effect. Use this option only in combination\n ");
+ printf("with -r and -w, like -rL or -wL00\n");
+ lockFlag = 0;
+ }
+ if (eraseFlag) {
+ printf("You have selected option -E for erase, which has had no\n");
+ printf("effect. Options -w, -p and -f together cause an automatic\n");
+ printf("erase; -E should be used on its own or together with B if\n");
+ printf("you want a blank check afterwards.\n");
+ eraseFlag = 0;
+ }
+ if (fusesFlag) {
+ printf("You have selected option -F to read or write fuse bits,\n");
+ printf("which has had no effect. This option can only be used in\n");
+ printf("the commands -rF and -wF.\n");
+ fusesFlag = 0;
+ }
+ if (highFFlag) {
+ printf("You have selected option -H to read or write fuse bits,\n");
+ printf("which has had no effect. This option can only be used in\n");
+ printf("the commands -rH and -wH.\n");
+ highFFlag = 0;
+ }
+ if (extdFFlag) {
+ printf("You have selected option -X to read or write extended fuses,\n");
+ printf("which has had no effect. This option can only be used in\n");
+ printf("the commands -rX and -wX.\n");
+ extdFFlag = 0;
+ }
+ if (calibFlag) {
+ printf("You have selected option -c to read the calibration byte,\n");
+ printf("which has had no effect. This option can only be used in\n");
+ printf("the command -rc.\n");
+ calibFlag = 0;
+ }
+}
+
+/* Merely check whether option -B was used, */
+/* and say something about it. */
+
+void reportBflag(void) {
+
+ if (blankChkFlag) {
+ printf("You have selected option -B for blank check, which has had \n");
+ printf("no effect. Options -w, -p and -f together cause an automatic\n");
+ printf("erase; if you also want a blank check before programming,\n");
+ printf("use -Bwpf. Use -EB if you just want an erase followed by a\n");
+ printf("blank check, or -B on its own for a blank check without a\n");
+ printf("preceding erase.\n");
+ blankChkFlag = 0;
+ }
+}
+
+/* Blank check the device. */
+
+int blank_check(void) {
+
+ int signal = 0;
+
+ printf("Performing blank check...\n");
+ signal = blankCheck(device.flashLimit, device.eepromLimit);
+ if (signal == 0)
+ printf("The program and eeprom area's are blank.\n");
+ if ((signal & 0x01) == 1)
+ printf("The program area is NOT blank.\n");
+ if ((signal & 0x02) == 2)
+ printf("The eeprom area is NOT blank.\n");
+ return(signal);
+}
+
+/* The command processor */
+
+void processCommand(void) {
+
+ unsigned long command;
+ long address;
+ int error; /* Storing return value */
+ int deviceCode = 0;
+ char binary[9] = "76543210"; /* Stores conversion num to binary */
+ int rFlag = 0;
+ int idx = 0;
+
+ /*
+ * If it hasn't been done yet, then set the device parameters
+ * according to device code and option -i[NN]
+ */
+ if (!device.initPerformed) {
+ printf("Enabling AVR serial reading/programming...\n");
+ /*
+ * First see if it's a 1200, which doesn't echo 0x53
+ * on the third byte of PROGRAM_ENABLE.
+ * Initiating contact at low speed
+ */
+ strcpy(device.name, "AT90S1200(A)");
+ setSckTiming(0.1);
+ enableSPI(1200);
+ deviceCode = setDevicePars(initFlag, identifier);
+ if (deviceCode == 1) {
+ fprintf(stderr, "_sp12dev not found\n");
+ exitSp12(2);
+ }
+ if (deviceCode == 2) {
+ fprintf(stderr, "_sp12dev corrupt.\n");
+ exitSp12(2);
+ }
+ if (deviceCode == 3) {
+ /*
+ * It doesn't look like a 1200. If a uC is connected,
+ * the SPI shiftregister is not in synch. We'll now fail
+ * if it is an out of synch 1200, but other uCs do
+ * echo 0x53 on the third byte of PROGRAM_ENABLE.
+ * So we'll try synch pulses for a while.
+ */
+ enableSPI(0);
+ deviceCode = setDevicePars(initFlag, identifier);
+ if (deviceCode == 1) {
+ fprintf(stderr, "_sp12dev not found\n");
+ exitSp12(2);
+ }
+ if (deviceCode == 2) {
+ fprintf(stderr, "_sp12dev corrupt.\n");
+ exitSp12(2);
+ }
+ if (device.sigByte_0 == 0 && device.sigByte_1 == 1
+ && device.sigByte_2 == 2) {
+ printf("WARNING: You have connected an unidentifiable\n");
+ printf(" device, which may be locked mode 3. \n");
+ printf(" Access to a supported, mode 3 locked \n");
+ printf(" device can be regained, but only by \n");
+ printf(" erasing it first.\n");
+ printf(" Check SP12.doc for details.\n\n");
+ }
+ }
+ if (initFlag == 0) {
+ printf("The device code bytes 0,1,2: %#x, %#x, %#x were read\n",
+ device.sigByte_0, device.sigByte_1, device.sigByte_2);
+ printf("from parallel port %#x and indicate the following:\n",
+ portAddress);
+ printf("You have connected an %s\n", device.name);
+ printf("The device was made by %s\n\n", device.madeBy);
+ }
+ if (device.flashLimit == 0 || device.eepromLimit == 0) {
+ printf("Nothing to do for sp12.\n");
+ exitSp12(1);
+ }
+ if (initFlag != 0 && device.flashLimit != 0) {
+ printf("Device code check OVERRULED! Assuming an %s\n\n",
+ device.name);
+ }
+ /*
+ * Continue with the Sck wave form for the now known uC,
+ * at the default speed.
+ */
+ setSckTiming(clockSpdDefault);
+ }
+ /*
+ * Do a few command sanity checks
+ */
+ error = flashFlag + eepromFlag + writeFlag + readFlag + lockFlag \
+ + fusesFlag + highFFlag + extdFFlag + calibFlag + fileFlag \
+ + addressFlag + eraseFlag + blankChkFlag + checksumFlag + testFlag \
+ + portFlag + optimizeFlag + overlayFlag;
+ if (((fusesFlag || highFFlag || extdFFlag || lockFlag || calibFlag) \
+ && error > 2) || (!readFlag && !writeFlag)) {
+ if (fusesFlag) {
+ printf("Use F only in the commands -rF or -wF<fuses>\n");
+ exitSp12(2);
+ }
+ if (highFFlag) {
+ printf("Use H only in the commands -rH or -wH<fuses>\n");
+ exitSp12(2);
+ }
+ if (extdFFlag) {
+ printf("Use X only in the commands -rX or -wX<fuses>\n");
+ exitSp12(2);
+ }
+ if (lockFlag) {
+ printf("Use L only in the commands -rL or -wL<fuses>\n");
+ exitSp12(2);
+ }
+ if (calibFlag) {
+ printf("Use c only in the command -rc[n]\n");
+ exitSp12(2);
+ }
+ }
+ if (flashFlag && eepromFlag) {
+ printf("Do not use options -p and -e in a single command.\n");
+ exitSp12(2);
+ }
+ if (checksumFlag && !eepromFlag && !flashFlag) {
+ printf("Use C only in commands like -Ce, -Cp, -Cef <file>\n");
+ exitSp12(2);
+ }
+ /*
+ * Inititalize buffers
+ */
+ for (address = 0; address < device.flashLimit; address++)
+ flashBuf[address] = 0xFFFF;
+ for (address = 0; address < device.eepromLimit; address++)
+ eepromBuf[address] = 0xFFF;
+ /*
+ * Set Sck timing to match the now known uC's wave form
+ * and clock speed asked for by an -Mn.n command.
+ */
+ if (clkSpeed && clkSpeed != prevClkSpeed) {
+ printf("Sck timing set for %.1f MHz or higher.\n",
+ setSckTiming(clkSpeed));
+ prevClkSpeed = clkSpeed;
+ }
+ clkSpeed = 0;
+ /*
+ * if r --> p || e || F || L
+ * rp --> rpf --> read program area; write to file
+ * rpa --> read program address to stdout
+ * rp --> read program area to stdout
+ * re --> ref --> read eeprom area; write to file
+ * rea --> read eeprom address to stdout
+ * re --> read eeprom area to stdout
+ * rF --> read the fuse bits, if possible
+ * rL --> read the lock bits, if possible
+ * check for C
+ * check for E, B, R, S, but just report them
+ */
+ if (readFlag && writeFlag) {
+ printf("Do not use options -w and -r in a single command.\n");
+ exitSp12(2);
+ }
+ if (readFlag) {
+ readFlag = 0;
+ if ((!pathAvailable || addressAvailable) && IntelFlag) {
+ printf("The Intel hex format is only available when writing\n");
+ printf("an entire memory area to file.\n");
+ exitSp12(2);
+ }
+ if (addressAvailable && hexAsciiFlag) {
+ printf("A hex dump with ascii translation is only possible\n");
+ printf("when writing an entire memory area to file.\n");
+ exitSp12(2);
+ }
+ if (IntelFlag && hexAsciiFlag) {
+ printf("Select either (I)ntel hex format or (h)ex dump with\n");
+ printf("ascii translation; not both at the same time\n");
+ exitSp12(2);
+ }
+ if (flashFlag) {
+ flashFlag = 0;
+ if (addressAvailable) {
+ addressAvailable = 0;
+ flashData = readCodeWord(flashAddress);
+ printf("The word %#06x was read from program address %#08x\n",
+ flashData, flashAddress);
+ } else {
+ printf("Reading program area.\n");
+ readFlashArea(flashBuf, device.flashLimit);
+ if (pathAvailable) {
+ pathAvailable = 0;
+ printf("Writing program area content to %s\n", flashPath);
+ if (fileFlashBuf(flashBuf, device.flashLimit,
+ flashPath, IntelFlag, hexAsciiFlag) != 0) {
+ printf("%s %s\n", FILE_WRITE_ERROR, flashPath);
+ exitSp12(2);
+ }
+ IntelFlag = 0;
+ hexAsciiFlag = 0;
+ } else {
+ if (hexAsciiFlag) {
+ printBuffer(flashBuf, device.flashLimit, 1);
+ hexAsciiFlag = 0;
+ } else {
+ for (address = 0; address < device.flashLimit; address++)
+ printf("%06lx:%04x\n", address, flashBuf[address]);
+ }
+ }
+ }
+ if (checksumFlag) {
+ checksumFlag = 0;
+ readFlashArea(flashBuf, device.flashLimit);
+ printf("Checksum program area: %04x\n",
+ checksum(flashBuf, device.flashLimit));
+ }
+ }
+ if (eepromFlag) {
+ eepromFlag = 0;
+ if (addressAvailable) {
+ addressAvailable = 0;
+ eepromData = readDataByte(eepromAddress);
+ num2bin((unsigned char) eepromData, binary);
+ printf("The byte %#04x (%d, %#o, B%s) ", eepromData, eepromData,
+ eepromData, binary);
+ printf("was read from eeprom address %#06x\n", eepromAddress);
+ } else {
+ printf("Reading eeprom area.\n");
+ readEepromArea(eepromBuf, device.eepromLimit);
+ if (pathAvailable) {
+ pathAvailable = 0;
+ printf("Writing eeprom area content to %s\n", eepromPath);
+ if (fileEepromBuf(eepromBuf, device.eepromLimit,
+ eepromPath, IntelFlag, hexAsciiFlag) != 0) {
+ printf("%s %s\n", FILE_WRITE_ERROR, eepromPath);
+ exitSp12(2);
+ }
+ IntelFlag = 0;
+ hexAsciiFlag = 0;
+ } else {
+ if (hexAsciiFlag) {
+ printBuffer(eepromBuf, device.eepromLimit, 0);
+ hexAsciiFlag = 0;
+ } else {
+ printf("Address: Data in hex, dec, oct, bin\n");
+ for (address = 0; address < device.eepromLimit; address++) {
+ num2bin((unsigned char) eepromBuf[address], binary);
+ printf( " %#04lx: %#04x %3d %#4o %s\n",
+ address, eepromBuf[address], eepromBuf[address],
+ eepromBuf[address], binary);
+ }
+ }
+ }
+ }
+ if (checksumFlag) {
+ checksumFlag = 0;
+ readEepromArea(eepromBuf, device.eepromLimit);
+ printf("Checksum eeprom area: %04x\n",
+ checksum(eepromBuf, device.eepromLimit));
+ }
+ }
+ if (fusesFlag) {
+ fusesFlag = 0;
+ rFlag = readFuses(READ_FUSES, fusesBits, R_FUSESLSB, R_FUSESLEN);
+ if (rFlag == 9) {
+ fprintf(stderr, "For the %s: Read_fuses not defined in _sp12dev.\n",
+ device.name);
+ } else {
+ printf("%s are the fuse bits read from an %s\n",
+ fusesBits, device.name);
+ printf("%s\n", FUSES_MESSAGE);
+ }
+ }
+ if (highFFlag) {
+ highFFlag = 0;
+ rFlag = readFuses(READ_HIGH_FUSES, fusesBits, RH_FUSESLSB, RH_FUSESLEN);
+ if (rFlag == 9) {
+ fprintf(stderr, "For the %s: Read_high_fuses not defined in _sp12dev.\n",
+ device.name);
+ } else {
+ printf("%s are the high fuse bits read from an %s\n",
+ fusesBits, device.name);
+ printf("%s\n", H_FUSES_MESSAGE);
+ }
+ }
+ if (extdFFlag) {
+ extdFFlag = 0;
+ rFlag = readFuses(READ_EXTD_FUSES, fusesBits, RX_FUSESLSB, RX_FUSESLEN);
+ if (rFlag == 9) {
+ fprintf(stderr, "For the %s: Read_extd_fuses not defined in _sp12dev.\n",
+ device.name);
+ } else {
+ printf("%s are the extended fuse bits read from an %s\n",
+ fusesBits, device.name);
+ printf("%s\n", X_FUSES_MESSAGE);
+ }
+ }
+ if (lockFlag) {
+ lockFlag = 0;
+ rFlag = readFuses(READ_LOCK, lockBits, R_LOCKLSB, R_LOCKLEN);
+ if (rFlag == 9) {
+ fprintf(stderr, "For the %s: Read_locks not defined in _sp12dev.\n",
+ device.name);
+ } else {
+ printf("%s are the lock bits read from an %s\n", lockBits, device.name);
+ printf("%s\n", LOCK_MESSAGE);
+ }
+ }
+ if (calibFlag) {
+ calibFlag = 0;
+ command = READ_CALIBRATION | (calibAddress << 8);
+ rFlag = readFuses(command, calibByte, CALIBLSB, CALIBLEN);
+ if (rFlag == 9) {
+ fprintf(stderr, "For the %s: Read_calibration not defined in _sp12dev.\n",
+ device.name);
+ } else {
+ printf("0x%02lx is the calibration byte read from address 0x%02lx\n",
+ strtol(calibByte, NULL, 2), calibAddress);
+ printf("%s\n", CALIB_MESSAGE);
+ }
+ }
+
+ reportLEFHXcFlag();
+ reportBflag();
+ }
+ /*
+ * If w --> p || e
+ * wp --> f || a
+ * wpf --> chip erase;
+ * write file to program area
+ * wpa --> NO chip erase (warning);
+ * write address:data
+ * we --> f || a
+ * wef --> write file to eeprom area
+ * wpa --> write address:data
+ * wF --> write the fuse bits, if possible
+ * wL --> write the lock bits, if possible
+ * check for C; complete write before reading p || e
+ * back into array to compute the checksum
+ * check for E, R, S but just report them
+ */
+ if (writeFlag) {
+ writeFlag = 0;
+ timeConst.byteWrite = timeConst.byteWriteDefault;
+ timeConst.byteWriteAdjusted = 0;
+ timeConst.pageWrite = timeConst.pageWriteDefault;
+ timeConst.pageWriteAdjusted = 0;
+ writeRetries = 0;
+ if (flashFlag) {
+ flashFlag = 0;
+ if (addressAvailable) {
+ addressAvailable = 0;
+ if (device.pageMode) {
+ printf ("ERROR: Single address flash programming "
+ "not available in page mode.\n");
+ exitSp12(2);
+ }
+ printf("WARNING: writing to single address in program area.\n");
+ printf(" Chip erase is not automatic; unless you \n");
+ printf(" used a separate -E command, it may not be \n");
+ printf(" possible to correctly write new data.\n");
+ printf("Writing %#06x into address %#08x in program area.\n",
+ flashData, flashAddress);
+ error = writeFlashVerified(flashAddress, flashData, optimizeFlag);
+ if (logWrites(commandStr, flashAddress, flashData, flashPath,
+ flashBuf, overlayFlag, &error) != 0)
+ printf("%s %s\n", FILE_WRITE_ERROR, "sp12log.txt");
+ if (error) {
+ printf("Readback ERROR at program address %#06x\n",
+ flashAddress);
+ exitSp12(3);
+ }
+
+ } else if (pathAvailable) { /* Write file to flash ////////////*/
+ pathAvailable = 0;
+ if (!overlayFlag) {
+ printf("Performing chip erase...\n");
+ eraseDevice();
+ }
+ if (blankChkFlag) {
+ blankChkFlag = 0;
+ if(blank_check())
+ exitSp12(3);
+ }
+ /* Read the HEX file */
+ printf("Writing content of %s into program area.\n", flashPath);
+ error = readFlashFile(flashPath, flashBuf, device.flashLimit);
+ if (error) {
+ printf("%s %s\n", flashPath, FILE_ERROR);
+ exitSp12(2);
+ }
+ /* Now copy file to flash */
+ error = writeFlashArea(flashBuf, device.flashLimit, optimizeFlag);
+ if (logWrites(commandStr, flashAddress, flashData, flashPath,
+ flashBuf, overlayFlag, &error) != 0)
+ printf("%s %s\n", FILE_WRITE_ERROR, "sp12log.txt");
+ if (error) {
+ printf("Readback ERROR after %d write retries.\n", writeRetries);
+ if (overlayFlag)
+ printf("The error may be due to an overlay conflict.\n");
+ exitSp12(3);
+ } else {
+ printf("%s written and verified.\n", flashPath);
+ printf("write retries: %d\n", writeRetries);
+ }
+ } /* Up to here the file to flash functions///////////////////*/
+
+ if (checksumFlag) {
+ checksumFlag = 0;
+ readFlashArea(flashBuf, device.flashLimit);
+ printf("Checksum program area: %04x\n",
+ checksum(flashBuf, device.flashLimit));
+ }
+ }
+ if (eepromFlag) {
+ eepromFlag = 0;
+ if (addressAvailable) {
+ addressAvailable = 0;
+ printf("Writing %#x into address %#x in eeprom area.\n",
+ eepromData, eepromAddress);
+ error=writeEepromVerified(eepromAddress, eepromData, optimizeFlag);
+ if (logWrites(commandStr, eepromAddress, eepromData, eepromPath,
+ eepromBuf, overlayFlag, &error) != 0)
+ printf("%s %s\n", FILE_WRITE_ERROR, "sp12log.txt");
+ if (error) {
+ printf("Readback ERROR at eeprom address %#04x\n",
+ eepromAddress);
+ exitSp12(3);
+ }
+ } else if (pathAvailable) {
+ pathAvailable = 0;
+ printf("Writing content of %s into eeprom area.\n", eepromPath);
+ error = readEepromFile(eepromPath, eepromBuf, device.eepromLimit);
+ if (error) {
+ printf("%s %s\n", eepromPath, FILE_ERROR);
+ exitSp12(2);
+ }
+ error = writeEepromArea(eepromBuf, device.eepromLimit,
+ optimizeFlag, overlayFlag);
+ if (logWrites(commandStr, eepromAddress, eepromData, eepromPath,
+ eepromBuf, overlayFlag, &error) != 0)
+ printf("%s %s\n", FILE_WRITE_ERROR, "sp12log.txt");
+ if (error) {
+ printf("Readback ERROR after %d write retries.\n", writeRetries);
+ exitSp12(3);
+ } else {
+ printf("%s written and verified.\n", eepromPath);
+ printf("write retries: %d, byteWrite: %ld percent of default\n",
+ writeRetries,
+ (timeConst.byteWrite * 100) / timeConst.byteWriteDefault);
+ }
+ }
+ if (checksumFlag) {
+ checksumFlag = 0;
+ readEepromArea(eepromBuf, device.eepromLimit);
+ printf("Checksum eeprom area: %04x\n",
+ checksum(eepromBuf, device.eepromLimit));
+ }
+ reportBflag();
+ }
+ if (lockFlag) {
+ lockFlag = 0;
+ logLocks(flashBuf, lockBits);
+ rFlag = writeFuses(WRITE_LOCK_HM, WRITE_LOCK_LM, lockBits, \
+ W_LOCKLSB, W_LOCKLEN);
+ if (rFlag == 9) {
+ fprintf(stderr, "For the %s: Write_lock not defined in _sp12dev.\n",
+ device.name);
+ } else if (rFlag == 2) {
+ fprintf(stderr, "For the %s: Wrong number of lock bits.\n", device.name);
+ } else {
+ for (idx = 0; idx < R_LOCKLEN - W_LOCKLEN; idx++)
+ printf(" ");
+ printf("%s are the lock bits written into an %s\n%s\n",
+ lockBits, device.name, LOCK_MESSAGE);
+ }
+ }
+ if (fusesFlag) {
+ fusesFlag = 0;
+ rFlag = writeFuses(WRITE_FUSES_HM, WRITE_FUSES_LM, fusesBits, \
+ W_FUSESLSB, W_FUSESLEN);
+ if (rFlag == 9) {
+ fprintf(stderr, "For the %s: Write_fuses not defined in _sp12dev.\n",
+ device.name);
+ } else if (rFlag == 2) {
+ fprintf(stderr, "For the %s; Wrong number of fuse bits.\n", device.name);
+ } else {
+ for (idx = 0; idx < (R_FUSESLEN - W_FUSESLEN) - W_FUSESLSB; idx++)
+ printf(" ");
+ printf("%s are the fuse bits written into an %s\n%s\n",
+ fusesBits, device.name, FUSES_MESSAGE);
+ }
+ }
+ if (highFFlag) {
+ highFFlag = 0;
+ rFlag = writeFuses(WRITE_HIGH_FUSES_HM, WRITE_HIGH_FUSES_LM, fusesBits, \
+ WH_FUSESLSB, WH_FUSESLEN);
+ if (rFlag == 9) {
+ fprintf(stderr, "For the %s: Write_high_fuses not defined in _sp12dev.\n",
+ device.name);
+ } else if (rFlag == 2) {
+ fprintf(stderr, "For the %s; Wrong number of high fuse bits.\n",
+ device.name);
+ } else {
+ for (idx = 0; idx < (RH_FUSESLEN - WH_FUSESLEN) - WH_FUSESLSB; idx++)
+ printf(" ");
+ printf("%s are the high fuse bits written into an %s\n%s\n",
+ fusesBits, device.name, H_FUSES_MESSAGE);
+ }
+ }
+ if (extdFFlag) {
+ extdFFlag = 0;
+ rFlag = writeFuses(WRITE_EXTD_FUSES_HM, WRITE_EXTD_FUSES_LM, fusesBits, \
+ WX_FUSESLSB, WX_FUSESLEN);
+ if (rFlag == 9) {
+ fprintf(stderr, "For the %s: Write_extd_fuses not defined in _sp12dev.\n",
+ device.name);
+ } else if (rFlag == 2) {
+ fprintf(stderr, "For the %s; Wrong number of high fuse bits.\n",
+ device.name);
+ } else {
+ for (idx = 0; idx < (RX_FUSESLEN - WX_FUSESLEN) - WX_FUSESLSB; idx++)
+ printf(" ");
+ printf("%s are the extended fuse bits written into an %s\n%s\n",
+ fusesBits, device.name, X_FUSES_MESSAGE);
+ }
+ }
+ reportLEFHXcFlag();
+ overlayFlag = 0;
+ }
+ /*
+ * if C --> (p || e) || ((p || e) && f)
+ * Cp(f) --> read program area (file) into
+ * device.flashLimit word array;
+ * calculate checksum for array
+ * Ce(f) --> read eeprom area (file) into
+ * device.eepromLimit byte array;
+ * calculate checksum for array
+ * check for L, E, B, F, R, S but just report them
+ */
+ if (checksumFlag) {
+ checksumFlag = 0;
+ if (eepromFlag) {
+ eepromFlag = 0;
+ if (pathAvailable) {
+ pathAvailable = 0;
+ if (readEepromFile(checksumPath, eepromBuf,
+ device.eepromLimit) != 0) {
+ printf("%s %s\n", checksumPath, FILE_ERROR);
+ exitSp12(2);
+ } else {
+ printf("Checksum eeprom file %s: %04x\n",
+ checksumPath, checksum(eepromBuf, device.eepromLimit));
+ }
+ } else {
+ readEepromArea(eepromBuf, device.eepromLimit);
+ printf("Checksum eeprom area: %04x\n",
+ checksum(eepromBuf, device.eepromLimit));
+ }
+ } else if (flashFlag) {
+ flashFlag = 0;
+ if (pathAvailable) {
+ pathAvailable = 0;
+ if (readFlashFile(checksumPath, flashBuf, device.flashLimit) != 0) {
+ printf("%s %s\n", checksumPath, FILE_ERROR);
+ exitSp12(2);
+ } else {
+ printf("Checksum program file %s: %04x\n",
+ checksumPath, checksum(flashBuf, device.flashLimit));
+ }
+ } else {
+ readFlashArea(flashBuf, device.flashLimit);
+ printf("Checksum program area: %04x\n",
+ checksum(flashBuf, device.flashLimit));
+ }
+ } else {
+ printf("you have selected option -C, but there is nothing\n");
+ printf("on the command line to calculate a checksum for.\n");
+ }
+ reportLEFHXcFlag();
+ reportBflag();
+ }
+ /*
+ * If E --> chip erase
+ */
+ if (eraseFlag) {
+ eraseFlag = 0;
+ printf("Performing chip erase...\n");
+ eraseDevice();
+ }
+ /*
+ * if B --> blank check
+ */
+ if (blankChkFlag) {
+ blankChkFlag = 0;
+ blank_check();
+ }
+ /*
+ * if i --> do nothing (init already done, device already reported)
+ * just clear initFlag
+ */
+ initFlag = 0;
+}
+
+int main(int argc, char *argv[])
+{
+ int ascii; /* characters on command line */
+ int idx = 0; /* index arguments on command line */
+ char *dataPtr; /* for separating address:data */
+ int exitFlag = 0;
+ char binary[9] = "76543210"; /* Stores conversion num to binary */
+
+ device.initPerformed = 0;
+ flashPath[0] = '\0';
+ eepromPath[0] = '\0';
+ checksumPath[0] = '\0';
+ sp12Start = clock();
+
+#ifdef LINUX
+ /*
+ * Take control of LPT I/O ports
+ */
+ if (ioperm(0x278,3,1)) {perror("ioperm error at 0x278");exit(1);}
+ if (ioperm(0x378,3,1)) {perror("ioperm error at 0x378");exit(1);}
+ if (ioperm(0x3BC,3,1)) {perror("ioperm error at 0x3BC");exit(1);}
+ /*
+ * Revoke setuid/setguid-root status
+ */
+ setgid(getgid());
+ setuid(getuid());
+#endif
+#ifdef WIN32
+ win_giveio (); // get access to I/O ports in Windows NT/2000/XP
+#endif
+
+ /*
+ * Check if something is on the command line, print usage message
+ * if not, else do init; if in sp12 mode, put power on the device
+ * (using Centronics data bits as power source), enable serial
+ * reading/programming
+ */
+ if (argc < 2) {
+ printf(USAGE, VERSION);
+ exit(0);
+ } else {
+ printf("SP12 version %s performing init...\n", VERSION);
+ initSp12(argv[0]);
+ }
+ /*
+ * allocate our memory buffers
+ */
+ flashBuf = malloc(FLASHBUF_UPPERLIMIT * sizeof (unsigned int));
+ if (flashBuf == NULL) {
+ printf ("ERROR: No memory available for buffer!\n");
+ exitSp12(2);
+ }
+ eepromBuf = malloc(EEPROMBUF_UPPERLIMIT * sizeof (unsigned int));
+ if (eepromBuf == NULL) {
+ printf ("ERROR: No memory available for buffer!\n");
+ exitSp12(2);
+ }
+ /*
+ * Dissect the command line, acting on commands from left to right.
+ */
+ idx = 0;
+ while (++idx < argc) {
+ if (*argv[idx] != '-' && fileFlag) {
+ fileFlag = 0;
+ if (addressFlag) {
+ printf("Do not use options -f and -a in a single command.\n");
+ exitSp12(2);
+ }
+ if (checksumFlag && !readFlag && !writeFlag) {
+ strcpy(checksumPath, argv[idx]);
+ pathAvailable = 1;
+ processCommand();
+ } else if (flashFlag) {
+ strcpy(flashPath, argv[idx]);
+ pathAvailable = 1;
+ processCommand();
+ } else if (eepromFlag) {
+ strcpy(eepromPath, argv[idx]);
+ pathAvailable = 1;
+ processCommand();
+ } else {
+ printf("Option -f must be combined with option -e, -p or -C\n");
+ exitSp12(2);
+ }
+ } else if (*argv[idx] != '-' && addressFlag) {
+ addressFlag = 0;
+ dataPtr = argv[idx];
+ if (writeFlag && (dataPtr = strchr(argv[idx], ':')) == NULL) {
+ printf("Option -wa must be followed by address:data\n");
+ exitSp12(2);
+ }
+ if (writeFlag)
+ dataPtr[0] = '\0';
+ if (flashFlag) {
+ flashAddress =str2num(argv[idx]);
+ if (writeFlag)
+ flashData = str2num(++dataPtr);
+ addressAvailable = 1;
+ processCommand();
+ } else if (eepromFlag) {
+ eepromAddress =str2num(argv[idx]);
+ if (writeFlag)
+ eepromData = str2num(++dataPtr);
+ addressAvailable = 1;
+ processCommand();
+ } else {
+ printf("Option -a must be combined with option -e or -p\n");
+ exitSp12(2);
+ }
+ } else if (*argv[idx] != '-') {
+ printf("No -a or -f option preceding argument `%s'\n", argv[idx]);
+ }
+ if (*argv[idx] == '-') {
+ if (strchr(argv[idx], 'w') != NULL)
+ strcpy(commandStr, argv[idx]);
+ while ((ascii = *++argv[idx]) != '\0') {
+ switch(ascii) {
+ case 'i':
+ if (strlen(argv[idx]) > 1) {
+ initFlag = 1;
+ strcpy(identifier, ++argv[idx]);
+ argv[idx][1] = '\0';
+ }
+ if (idx > 1) {
+ printf("WARNING: option -i[NN] has no effect unless\n");
+ printf(" it is first on the command line.\n");
+ }
+ break;
+ case 'T':
+ if (strlen(argv[idx]) > 1)
+ testFlag = atoi(++argv[idx]);
+ break;
+ case 'M':
+ if (strlen(argv[idx]) > 1) {
+ clkSpeed = atof(++argv[idx]);
+ if (clkSpeed <= 0.1)
+ clkSpeed = 0.1;
+ argv[idx][1] = '\0';
+ }
+ break;
+ case 'P':
+ if (strlen(argv[idx]) > 1) {
+ if (argv[idx][2] == 'x')
+ portFlag = (int) strtol(++argv[idx], NULL, 16);
+ else
+ portFlag = (int) strtol(++argv[idx], NULL, 10);
+ if ((idx + 1) == argc)
+ exitFlag = 1;
+ else
+ exitFlag = 0;
+ portControl(portFlag, exitFlag);
+ num2bin((unsigned char) portFlag, binary);
+ printf("Writing %#04x (%d, B%s) ",
+ portFlag, portFlag, binary);
+ printf("to the parallel port data bits.\n");
+ if ((idx + 1) < argc)
+ portFlag = 0;
+ argv[idx][1] = '\0';
+ }
+ break;
+ case 'p':
+ flashFlag = 1;
+ break;
+ case 'e':
+ eepromFlag = 1;
+ break;
+ case 'w':
+ writeFlag = 1;
+ break;
+ case 'r':
+ readFlag = 1;
+ break;
+ case 'L':
+ if (strlen(argv[idx]) > 1 \
+ && (*(argv[idx] + 1) == '0' || *(argv[idx] + 1) == '1')) {
+ strcpy(lockBits, ++argv[idx]);
+ argv[idx][1] = '\0';
+ }
+ lockFlag = 1;
+ break;
+ case '0':
+ if (optimizeFlag)
+ optimizeFlag = 10;
+ break;
+ case '1':
+ if (optimizeFlag && !POLL_RDY_BSY)
+ optimizeFlag = 11;
+ break;
+ case '2':
+ break;
+ case '3':
+ break;
+ case '4':
+ break;
+ case '5':
+ break;
+ case '6':
+ break;
+ case '7':
+ break;
+ case '8':
+ break;
+ case '9':
+ break;
+ case 'f':
+ fileFlag = 1;
+ break;
+ case 'a':
+ addressFlag = 1;
+ break;
+ case 'E':
+ eraseFlag = 1;
+ break;
+ case 'B':
+ blankChkFlag = 1;
+ break;
+ case 'C':
+ checksumFlag = 1;
+ break;
+ case 'F':
+ if (strlen(argv[idx]) > 1 \
+ && (*(argv[idx] + 1) == '0' || *(argv[idx] + 1) == '1')) {
+ strcpy(fusesBits, ++argv[idx]);
+ argv[idx][1] = '\0';
+ }
+ fusesFlag = 1;
+ break;
+ case 'H':
+ if (strlen(argv[idx]) > 1 \
+ && (*(argv[idx] + 1) == '0' || *(argv[idx] + 1) == '1')) {
+ strcpy(fusesBits, ++argv[idx]);
+ argv[idx][1] = '\0';
+ }
+ highFFlag = 1;
+ break;
+ case 'X':
+ if (strlen(argv[idx]) > 1 \
+ && (*(argv[idx] + 1) == '0' || *(argv[idx] + 1) == '1')) {
+ strcpy(fusesBits, ++argv[idx]);
+ argv[idx][1] = '\0';
+ }
+ extdFFlag = 1;
+ break;
+ case 'I':
+ IntelFlag = 1;
+ break;
+ case 'c':
+ if (strlen(argv[idx]) > 1 \
+ && (*(argv[idx] + 1) == '0' || *(argv[idx] + 1) == '1')) {
+ calibAddress = strtoul(++argv[idx], NULL, 2);
+ argv[idx][1] = '\0';
+ }
+ if (strlen(argv[idx]) > 1 \
+ && (*(argv[idx] + 1) != '0' && *(argv[idx] + 1) != '1')) {
+ calibAddress = strtoul(++argv[idx], NULL, 10);
+ argv[idx][1] = '\0';
+ }
+ calibFlag = 1;
+ break;
+ case 'h':
+ hexAsciiFlag = 1;
+ break;
+ case 'o':
+ optimizeFlag = 1;
+ break;
+ case 's':
+ supported();
+ exitSp12(0);
+ break;
+ case 't':
+ idx = 0;
+ printf("(-t)iming check, about 10 seconds\n");
+ printf("..................................................\r");
+ for (idx = 0; idx < 50; idx++) {
+ loopDelay(timeConst.resetPuls * 4);
+ putchar('o');
+ fflush(stdout);
+ }
+ printf(" STOP\n");
+ exitSp12(0);
+ break;
+ case 'O':
+ overlayFlag = 1;
+ break;
+ default:
+ printf("The character `%c' ", ascii);
+ printf("is not a valid option.\n");
+ break;
+ }
+ }
+ if (!fileFlag && !addressFlag)
+ processCommand();
+ }
+ }
+ if (fileFlag) {
+ printf("Option f used, but no path on command line.\n");
+ printf("Nothing to do for sp12.\n");
+ exitSp12(2);
+ }
+ /*
+ * Exiting with the byte `portFlag' on the parallel port data bits.
+ * When the -Tnn option is not (also) used as the final option on
+ * the command line, this defaults to 0x00.
+ */
+ if (SCK == S_SCK && testFlag) {
+ switch(testFlag) {
+ case 1:
+ portFlag = RESET;
+ break;
+ case 2:
+ portFlag = RESET | PORTPOWER;
+ break;
+ default:
+ break;
+ }
+ } else if (testFlag) {
+ printf("Option -T is not available in Kanda compatible mode.\n");
+ }
+ if (!portFlag) {
+ portFlag = DEFAULT_EXIT_STATE;
+ num2bin((unsigned char) portFlag, binary);
+ printf("Writing %#04x (%d, B%s) ",
+ portFlag, portFlag, binary);
+ printf("to the parallel port data bits.\n");
+ }
+ portControl(portFlag, 1);
+ if (SCK == S_SCK) {
+ if (portFlag & 0x02)
+ printf("Reset was left high.\n");
+ if (portFlag & 0x7C)
+ printf("One or more `power' bits were left high.\n");
+ }
+ sp12End = clock();
+ sp12Elapsed = ((double) (sp12End - sp12Start)) / CLOCKS_PER_SEC;
+ printf("Sp12 was active for %#3.2f seconds.\n", sp12Elapsed);
+ return(0);
+}
diff --git a/src/sp12.h b/src/sp12.h
new file mode 100644
index 0000000..a75ef18
--- /dev/null
+++ b/src/sp12.h
@@ -0,0 +1,520 @@
+/* SP12: A serial programmer for working with Atmel AVR uCs */
+/* Copyright (C) 1997-2008 Ken Huntington, Kevin Towers, */
+/* Artur Pundsack, Pitronics. */
+
+/* This program 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. */
+
+/* This program 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 this program; if not, write to the Free Software */
+/* Foundation, Inc., 59 Temple Place - Suite 330, Boston, */
+/* MA 02111-1307, USA. */
+
+/* Pitronics can be reached by email: sbolt@xs4all.nl */
+/* Kevin Towers can be reached by email: ktowers@omnexcontrols.com */
+/* Ken Huntington can be reached by email: kenh@compmore.net */
+/* Artur Pundsack can be reached by email: ap@pa-tec.de */
+
+#define WORDLEN 16
+#define DATALEN 5
+#define FLASH_DATAPTR 6
+#define MAXLEN 500
+#define FLASHBUF_UPPERLIMIT 1048576L
+#define EEPROMBUF_UPPERLIMIT 8192L
+#define FILE_ERROR "does not exist, wrong format,\n address > limit, or HEX file checksum failure."
+#define FILE_WRITE_ERROR "Unable to write to, or finish writing to"
+
+#define STK200TEST 0x02 /* data-1 connected to status-7 */
+#define STK200PRESENT 0x80
+#define STK300TEST 0x01 /* data-0 connected to status-5 */
+#define STK300PRESENT 0x20
+/*
+ * program control line definitions for sp12 cable/dongle
+ */
+#define S_SCK 0x01 /* SPI clock out */
+#define S_MOSI 0x80 /* SPI data out */
+#define S_MISO_BITNR 7 /* SPI data in bit number */
+#define S_MISO_INV 0xFF /* inverted */
+#define S_RESET 0x02 /* processor reset control */
+#define S_ENABLE 0 /* Dummy */
+#define S_PORTPOWER 0x7C /* `port power' bits */
+#define S_DEFAULT_EXIT_STATE 0
+#define S_PORTACTIVE 0x7C /* reserved for portControl() */
+/*
+ * program control line definitions for Kanda STK200 dongle
+ */
+#define K_SCK 0x10 /* SPI clock out */
+#define K_MOSI 0x20 /* SPI data out */
+#define K_MISO_BITNR 6 /* SPI data in bit number */
+#define K_MISO_INV 0x00 /* not inverted */
+#define K_RESET 0x80 /* processor reset control */
+#define K_ENABLE 0x0C /* ~enables Kanda `dongle' */
+#define K_PORTPOWER 0 /* Dummy */
+#define K_DEFAULT_EXIT_STATE 0x0C
+#define K_PORTACTIVE 0x43
+
+/*
+ * These macros perform various bit operations on scalar types using bit masks
+ */
+#define bit_set(value, bit) ((value) |= (bit))
+#define bit_clear(value, bit) ((value) &= ~(bit))
+#define bit_comp(value, bit) ((value) ^= (bit))
+#define bit_test(value, bit) (((value) & (bit)) != 0)
+
+/*---------------------- The global constants -----------------------*/
+/*----------------------- defined in init.c ------------------------*/
+
+extern unsigned long PROGRAM_ENABLE;
+extern unsigned long CHIP_ERASE;
+extern unsigned long READ_DEVICE;
+extern unsigned long READ_PROG_LO;
+extern unsigned long READ_PROG_HI;
+extern unsigned long WRITE_PROG_LO;
+extern unsigned long WRITE_PROG_HI;
+extern unsigned long LOAD_PAGE_HI;
+extern unsigned long LOAD_PAGE_LO;
+extern unsigned long LOAD_EXT_ADDR; /* AP, 15.01.2008: Added */
+extern unsigned long WRITE_PAGE;
+extern unsigned long READ_EEPROM;
+extern unsigned long WRITE_EEPROM;
+extern unsigned long POLL_RDY_BSY;
+extern unsigned long WRITE_LOCK_HM;
+extern unsigned long WRITE_LOCK_LM;
+extern unsigned long READ_LOCK;
+extern unsigned long WRITE_FUSES_HM;
+extern unsigned long WRITE_FUSES_LM;
+extern unsigned long READ_FUSES;
+extern unsigned long WRITE_HIGH_FUSES_HM;
+extern unsigned long WRITE_HIGH_FUSES_LM;
+extern unsigned long READ_HIGH_FUSES;
+extern unsigned long WRITE_EXTD_FUSES_HM;
+extern unsigned long WRITE_EXTD_FUSES_LM;
+extern unsigned long READ_EXTD_FUSES;
+extern unsigned long READ_CALIBRATION;
+extern char CALIB_MESSAGE[MAXLEN];
+extern char LOCK_MESSAGE[MAXLEN];
+extern char FUSES_MESSAGE[MAXLEN];
+extern char H_FUSES_MESSAGE[MAXLEN];
+extern char X_FUSES_MESSAGE[MAXLEN];
+extern int POLL_RBLSB;
+extern int POLL_RBLEN;
+extern int W_LOCKLSB;
+extern int R_LOCKLSB;
+extern int W_LOCKLEN;
+extern int R_LOCKLEN;
+extern int W_FUSESLSB;
+extern int R_FUSESLSB;
+extern int W_FUSESLEN;
+extern int R_FUSESLEN;
+extern int WH_FUSESLSB;
+extern int RH_FUSESLSB;
+extern int WH_FUSESLEN;
+extern int RH_FUSESLEN;
+extern int WX_FUSESLSB;
+extern int RX_FUSESLSB;
+extern int WX_FUSESLEN;
+extern int RX_FUSESLEN;
+extern int CALIBLSB;
+extern int CALIBLEN;
+
+/*-------------------- No more global constants ---------------------*/
+
+/*---------------------- The global variables -----------------------*/
+/*----------------------- defined in init.c ------------------------*/
+
+extern unsigned char SCK;
+extern unsigned char MOSI;
+extern unsigned char MISO_BITNR;
+extern unsigned char MISO_INV;
+extern unsigned char RESET;
+extern unsigned char ENABLE;
+extern unsigned char PORTPOWER;
+extern unsigned char DEFAULT_EXIT_STATE;
+extern unsigned char PORTACTIVE;
+
+struct timeConsts {
+ double loop;
+ double port; /* time taken by outportb() */
+ unsigned long sckLO;
+ unsigned long sckHI;
+ unsigned long resetPuls;
+ unsigned programEnable;
+ unsigned chipErase;
+ unsigned long byteWrite;
+ unsigned long byteWriteDefault;
+ int byteWriteAdjusted; /* a flag */
+ unsigned long pageWrite;
+ unsigned long pageWriteDefault;
+ int pageWriteAdjusted; /* a flag */
+ unsigned powerOn;
+};
+
+struct device_info {
+ unsigned char sigByte_0;
+ unsigned char sigByte_1;
+ unsigned char sigByte_2;
+ char name[30];
+ char madeBy[30];
+ long eepromLimit;
+ long flashLimit;
+ int initPerformed;
+ int pageMode; /* true if page programmed device */
+ int pageSize;
+ int fuseMode; /* 0: AT90S1200, 2313, 4414, 8515 */
+ /* 4: 2343 (lock +r, RCEN +rw) */
+ /* 5: 2323 4434, 8535 (lock +r, */
+ /* FSTRT +rw) */
+ /* 6: (ATmega103/603) */
+ /* 7: 2333, 4433 (lock +r, CKSELx3 +rw */
+ /* BODx2 +rw) */
+};
+
+struct logging {
+ char logPath[MAXLEN];
+ int logging;
+ int query;
+};
+
+extern struct timeConsts timeConst;
+extern struct device_info device;
+extern struct logging log;
+
+extern int portAddress;
+extern int portStatus;
+extern int writeRetries;
+extern float clockSpdDefault;
+extern int KandaMode; /* Cable/dongle Kanda or sp12 */
+extern int ExtAddr; /* AP, 15.01.2008: Added, Marker for crossing */
+ /* the 64k Flash boundary */
+
+extern unsigned char outbyte; /* current state of device */
+ /* control port */
+
+/*-------------------- No more global variables ---------------------*/
+
+/*
+ * A few prototypes.
+ */
+
+/* Look for the rc file, using environment variable `SP12' (if */
+/* this variable has not been set, the current directory is used). */
+/* If the rc file exists, then read the time constants and the */
+/* parallel port address from this file. If the rc file is not */
+/* found, then calibrate the time constants, check for available */
+/* parallel ports, set (usually the primary) address, and write */
+/* it all into a new rc file. Afterwards check if the parallel */
+/* port is actually there (guarding against a corrupt rc file, and */
+/* setting the port data register to 0xF0), and also check if the */
+/* time constants aren't 0. exit(1) if a check fails. */
+/* Based on either the KANDA setting in _sp12rc or the name by */
+/* which the program is called (sp12 or sp12.kanda), the */
+/* parallel port pinout is adapted to the cable/dongles supplied */
+/* with the Atmel/Kanda STK200/300 starter kits, or to the */
+/* original SP12 cable and Ken Huntington's dongle. */
+
+void initSp12(char *howWeAreCalled);
+
+
+/* Takes a devicecode (like 0392) or identifier ((-i)433) and sets */
+/* the device commands, messages and other parameters as found in */
+/* _sp12dev. */
+/* Returns 1 if _sp12dev not found; 2 if a line in _sp12dev is */
+/* corrupt; 3 if _sp12dev has no entry for the deviceCode or */
+/* identifier and the deviceCode (as an int) if all is well. */
+
+int setDevicePars(int initFlag, char *identifier);
+
+
+/* Collects all device codes and names from _sp12dev, and shows */
+/* them on screen. */
+/* Returns 1 if _sp12dev not found, else zero. */
+
+int supported(void);
+
+
+/* Delay by executing loops, not so empty as to be caught by */
+/* the compiler's optimization efforts. */
+
+void loopDelay(unsigned long loops);
+
+
+/* Determines the number of bits in command, and shifts them */
+/* highest bit first into unsigned char output, which then */
+/* is used to clock them out bit by bit through Centronics */
+/* data-7. */
+/* Reads back and returns whatever the uC clocks out on Miso */
+/* while the command is clocked in. */
+/* Power bits prior state assumed on; left on at return. */
+/* SCK prior state assumed lo; left lo at return. */
+
+unsigned long clockOutCommand(unsigned long command);
+
+
+/* Accepts a 32-bit address, and shifts it highest bit first */
+/* into unsigned char output, which then is used to clock the */
+/* bits out one by one hrough Centronics data-7 */
+/* Power bits prior state assumed on; left on at return. */
+/* SCK prior state assumed lo; left lo at return. */
+
+void clockOutAddress(unsigned long address);
+
+
+/* Read one data byte from the status register of the */
+/* Centronics port. */
+/* Power bits prior state assumed on; left on at return. */
+/* SCK prior state assumed lo; left lo at return. */
+
+unsigned char clockInByte(void);
+
+
+/* Accepts a 32-bit address. Reads the codeWord in two steps */
+/* from the device connected to the Centronics port. */
+/* Power bits prior state assumed on; left on at return. */
+/* SCK prior state assumed lo; left lo at return. */
+
+unsigned int readCodeWord(unsigned long address);
+
+
+/* Accepts a 16-bit codeWord and a 7-bit address. Writes the code */
+/* in two steps with no delays into the ATMega device connected to */
+/* the Centronics port. */
+/* Power bits prior state assumed on; left on at return. */
+/* SCK prior state assumed lo; left lo at return. */
+
+void loadCodePage(unsigned long address, unsigned int codeWord);
+
+
+/* Accepts a 32-bit page number and programs it into the ATMega */
+/* device connected to the Centronics parallel port. */
+/* The page is read back and compared with the buffer; if all */
+/* is well, 0 is returned, else 1. */
+/* Power bits prior state assumed on; left on at return. */
+/* SCK prior state assumed lo; left lo at return. */
+
+int writeCodePage(unsigned int flashBuf[], unsigned long address);
+
+
+/* Accepts a 16-bit codeWord and a 16-bit address. Writes the code */
+/* in two steps into the device connected to the Centronics port and */
+/* verifies arrival. Returns 1 if verify fails three times, else 0. */
+/* The time constant byteWrite is adjusted dynamically, */
+/* if the device is not a 1200(A) and optimizeFlag allows. */
+/* Power bits prior state assumed on; left on at return. */
+/* SCK prior state assumed lo; left lo at return. */
+/* AP, 15.01.2008: This function is only available for non page mode!*/
+
+int writeFlashVerified(unsigned int address, unsigned int codeWord,
+ int optimizeFlag);
+
+
+/* Expects flashBuf[] to be filled with data. */
+/* If a (page) write fails, 1 is returned, else 0. */
+/* Power bits prior state assumed on; left on at return. */
+/* SCK prior state assumed lo; left lo at return. */
+
+int writeFlashArea(unsigned int flashBuf[], long bufLimit,
+ int optimizeFlag);
+
+
+/* Assume the device connected to the Centronics port is powered, */
+/* but that device sck was not held low during power-up, so a */
+/* reset pulse is needed before the SPI can be enabled. */
+/* Assume also that power has yet to be switched on (for separate */
+/* programmer powered by parallel port, instead of in-circuit */
+/* programming. */
+/* If uC is 1200, emit just one PROGRAM_ENABLE command. */
+/* Else retry until the third byte read back by clockOutCommand() */
+/* is 0x53, or at least 32 retries have been unsuccessful. */
+
+int enableSPI(int uC);
+
+
+/* Adapt Sck timing to user's clock speed setting */
+/* (Init sets the timing equal to sck0.5M, and the flag will be */
+/* zero unless altered by the command line.) */
+
+float setSckTiming(float clkSpeed);
+
+
+/* Before writing to the flash area, the device has to be erased. */
+/* Note: This command also erases the eeprom area (all to 0xFF). */
+/* There is no need for general erase before writing to the */
+/* eeprom area, since the device has an auto-erase built into */
+/* the eeprom write cycle. Thus you can alter one or more eeprom */
+/* addresses without disturbing the rest of the device. */
+
+void eraseDevice(void);
+
+
+/* A routine to switch off the powerbits; useful only when the */
+/* separate programmer is used instead of in-circuit programming. */
+
+void portControl(int portFlag, int exitFlag);
+
+
+/* Sends a lock or fuses string to the microcontroller. */
+/* Returns 9 if the writeCommand is zero (not available according */
+/* to _sp12dev). */
+/* The lock or fuses string may represent a binary or hex number. */
+/* If it's binary, the length of the string is compared with */
+/* W_FUSESLEN; a useful sanity check. The function returns 2 if */
+/* the length is wrong. A hex number is not checked, as there is no */
+/* way to know how many leading zeroes are intended. */
+/* Next, the block is ored into the writeCommand and uploaded to */
+/* the device. */
+/* Since (re)enabling brown-out detection usually causes the */
+/* AT90(L)S2333/4433 to hang, the fuses are only written, not */
+/* verified. */
+
+int writeFuses(unsigned long commandHM, unsigned long commandLM, \
+ char *fuses, int WfusesLsb, int WfusesLen);
+
+
+/* Reads the lock or fuse bit block from the microcontroller. */
+/* Returns 9 if the readCommand is zero (not available according */
+/* to _sp12dev). */
+
+int readFuses(unsigned long readCommand, char *fuses, \
+ int RfusesLsb, int RfusesLen);
+
+void readDeviceCode(void);
+
+
+/* Converts address or data string to number, automatically */
+/* selecting hex (0xNNN), oct (0NNN), binary (BNNN) or */
+/* dec (NNN) input. */
+
+unsigned int str2num(char *addressOrData);
+
+
+/* convert unsigned int to a string-representation of the */
+/* binary number (ascii `0' is 48, ascii `1' is 49) */
+
+void num2bin(unsigned char buffer, char *binary);
+
+
+/* Reads program file in format address:data, stores data into */
+/* flashBuf by address. The two numbers must be hex without `0x' */
+/* for example: 00000c:99e1 */
+
+int readFlashFile(char flashPath[], unsigned int flashBuf[], long bufLimit);
+
+
+/* Reads program file in Atmel generic format (address:data) or */
+/* Intel HEX format; stores data into into flashBuf by address. */
+/* flashBuf by address. The two numbers in the generic format are */
+/* hex without `0x'. For example: 00000c:99e1 */
+
+int readEepromFile(char eepromPath[], unsigned int eepromBuf[], long bufLimit);
+
+
+/* Calculate checksum over buffer[]; bufLimit is number of */
+/* elements in buffer. */
+
+unsigned int checksum(unsigned int buffer[], long bufLimit);
+
+
+/* (expects flashBuf[] to be filled with 0xFFFF) */
+/* Loops readCodeWord into flashBuf[] */
+/* Power bits prior state assumed on; left on at return. */
+/* SCK prior state assumed lo; left lo at return. */
+
+void readFlashArea(unsigned int flashBuf[], long bufLimit);
+
+
+/* Writes flashBuf[] into FILE *flashBufPtr; */
+/* If write to file fails, return(1), else return(0) */
+
+int fileFlashBuf(unsigned int flashBuf[], long bufLimit,
+ char flashPath[], int intel_flag, int hex_ascii_flag);
+
+
+/* Reads flash and eeprom area's. Return 1 if a flash address does */
+/* not contain 0xFFFF, or 3 if an eeprom address also isn't blank; */
+/* return 2 if the flash is blank, but the eeprom isn't; */
+/* return 0 is both area's are blank. */
+/* Power bits prior state assumed on; left on at return. */
+/* SCK prior state assumed lo; left lo at return. */
+
+int blankCheck(long flashLimit, long eepromLimit);
+
+
+/* Accepts a dataByte and a 16-bit address. Writes the byte into */
+/* the device connected to the Centronics port and verifies arrival. */
+/* Returns 1 if verify fails three times, else 0. */
+/* The time constant byteWrite is adjusted dynamically, */
+/* if the device is not a 1200(A) and/or optimizeFlag allows. */
+/* Power bits prior state assumed on; left on at return. */
+/* SCK prior state assumed lo; left lo at return. */
+
+int writeByteVerified(unsigned long writeCommand,
+ unsigned long readCommand, int optimizeFlag);
+
+
+/* Writes eepromBuf[] into FILE *eepromBufPtr; */
+/* If write to file fails, return(1), else return(0) */
+
+int fileEepromBuf(unsigned int eepromBuf[], long bufLimit,
+ char eepromPath[], int intel_flag, int hex_ascii_flag);
+
+
+/* Accepts a 16-bit address. Reads the dataByte in two steps */
+/* from the device connected to the Centronics port. */
+/* Power bits prior state assumed on; left on at return. */
+/* SCK prior state assumed lo; left lo at return. */
+
+unsigned char readDataByte(unsigned int address);
+
+
+/* Writes buffer[] to stdout as a hex dump with ascii translation */
+
+void printBuffer(unsigned int buffer[], long bufLimit, int twoByteFlag);
+
+
+/* Add a line to the log about a write command */
+
+int logWrites(char *commandStr, unsigned int address, int data,
+ char *path, unsigned int buffer[], int overlayFlag, int *error);
+
+
+/* Add a line to the log about a lock command */
+
+int logLocks(unsigned int buffer[], char *lockBits);
+
+
+/* Accepts a dataByte and a 16-bit address. Writes the code in two */
+/* steps into the device connected to the Centronics port and */
+/* verifies arrival. Returns 1 if verify fails three times, else 0. */
+/* The time constant byteWrite is adjusted dynamically, */
+/* if the device is not a 1200(A) and optimizeFlag allows. */
+/* Power bits prior state assumed on; left on at return. */
+/* SCK prior state assumed lo; left lo at return. */
+
+int writeEepromVerified(unsigned int address, unsigned char dataByte,
+ int optimizeFlag);
+
+
+/* Expects eepromBuf[] to be filled with data. */
+/* executes writeEepromVerified() address by address; */
+/* if verify fails, stops and returns 1, else returns 0. */
+/* Power bits prior state assumed on; left on at return. */
+/* SCK prior state assumed lo; left lo at return. */
+
+int writeEepromArea(unsigned int eepromBuf[], long bufLimit,
+ int optimizeFlag, int overlayFlag);
+
+
+/* (expects eepromBuf[] to be filled with 0xFF) */
+/* Loops readDataByte into eepromBuf[] */
+/* Power bits prior state assumed on; left on at return. */
+/* SCK prior state assumed lo; left lo at return. */
+
+void readEepromArea(unsigned int EepromBuf[], long bufLimit);
+
diff --git a/src/winnt.c b/src/winnt.c
new file mode 100644
index 0000000..30000ae
--- /dev/null
+++ b/src/winnt.c
@@ -0,0 +1,75 @@
+/* SP12: A serial programmer for working with Atmel AT90S uCs */
+/* Copyright (C) 1997-2003 Ken Huntington, Kevin Towers, Pitronics. */
+
+/* This program 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. */
+
+/* This program 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 this program; if not, write to the Free Software */
+/* Foundation, Inc., 59 Temple Place - Suite 330, Boston, */
+/* MA 02111-1307, USA. */
+
+/* Pitronics can be reached by email: sbolt@xs4all.nl */
+/* Kevin Towers can be reached by email: ktowers@omnexcontrols.com */
+/* Ken Huntington can be reached by email: kenh@compmore.net */
+
+#ifdef WIN32
+
+#include <windows.h>
+#include <stdio.h>
+
+/*
+To install the device driver you need Dale Roberts' device driver giveio.sys
+(http://www.ddj.com/ftp/1996/1996.05/directio.zip) and the program instdrv.exe
+(instdrv.zip) from the device driver kit (DDK) for Windows NT.
+ Installation instructions
+
+ * Copy the driver giveio.sys from the archive directio.zip into %SystemRoot%\system32\drivers.
+ * Use command line to install the driver using instdrv:
+ instdrv giveio %SystemRoot%\system32\drivers\giveio.sys
+
+ * In order to start this driver at every reboot automatically, change the start up behaviour to automatic:
+ 2000/XP: start regedit and change the value of the key "Start" in
+ "HKEY_LOCAL_MACHINE\SYSTEM\CurrentControlSet\Services\giveio" to 2.
+ NT: Control Panel->Devices->giveio->Start Up->Automatic.
+
+*/
+
+void win_giveio(void)
+ {
+ HANDLE hDevice;
+
+ if(INVALID_HANDLE_VALUE==(hDevice=CreateFile("\\\\.\\giveio",GENERIC_READ,0,NULL,OPEN_EXISTING,FILE_ATTRIBUTE_NORMAL,NULL))){
+ fprintf(stderr, "Couldn't access giveio device\n");
+ exit(1);
+ }
+ CloseHandle(hDevice);
+ }
+
+
+void delay(int mseconds)
+ {
+ Sleep((DWORD)mseconds);
+ }
+
+inline unsigned char inportb (const unsigned short port)
+ {
+ register unsigned char val;
+
+ __asm __volatile ("inb %%dx,%%al" : "=a" (val) : "d" (port));
+ return val;
+ }
+
+inline void outportb (unsigned short port, const unsigned char val)
+ {
+ __asm __volatile ("outb %%al,%%dx" :: "a" (val) , "d" (port));
+ }
+#endif
+