參考資訊:
https://www.sparetimelabs.com/bl08/bl08.zip
https://www.nxp.com/docs/en/data-sheet/MC68HC908JB8.pdf
bl08.c
// TODO POSIX COMPATIBLE IOCTL
// Note page size for JB8 has been temporarily changed to 32
/*
File: bl08.c
Copyright (C) 2004,2008 Kustaa Nyholm
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.0 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
Lesser 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
*/
// This code compiles cleanly with i686-apple-darwin8-gcc-4.0.1 with command:
// gcc -Wall -std=c99 -pedantic -o bl08 bl08.c
// and uses only POSIX standard headers so this should be pretty
// easy to port anywhere: Mac OS X, Linux , MinGW, Cygwin
//
// Having said that I'm pretty sure there is implicit assumptions that
// int is 32 bits, char is signed 8 bits
// I also expect that extending this code to handle S-records in the +2G range
// will uncover more implicit assumptions.
//
// cheers Kusti
// additions ... override value for non supported CPUs
// reset control with DTR
#include <asm/ioctls.h>
#include <ctype.h>
#include <fcntl.h>
#include <stdarg.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <termios.h>
#include <time.h>
#include <unistd.h>
//#include <asm/termios.h>
#include <signal.h>
#include <sys/types.h>
#include <sys/wait.h>
// Terrible hack here because POSIX says 'ioctl()' is in <stropts.h> but
// for example Mac OS X Tiger does not have this header, OTOH I think
// that <sys/ictl.h> is not POSIX either so how do you write
// actual POSIX compliant code that compiles cleanly on POSIX...
extern int ioctl(int filedes, int command, ...);
// End of hack
#define PIDFILENAME "bl08PIDfile.temp"
int com = 0;
char* COM = "/dev/ttyUSBS0";
char version[] = "1.0.0.0";
unsigned char image[0x10000]; // HC908 memory image
// give some initial meaningfull values (based on MC68HC908GZ16)
int FLASH = 0xE000; // Flash start address
int PUTBYTE = 0xFEAF; // Receive byte routine address
int GETBYTE = 0x1C00; // Receive byte routine address
int RDVRRNG = 0x1C03; // Read/verify flash routine address
int ERARRNG = 0x1C06; // Erase flash routine address
int PRGRNGE = 0x1C09; // Flash programming routine address
int FLBPR = 0xFF7E; // Flash block proctection register address
int CPUSPEED =
8; // 2 x Fbus freq, e.g. ext osc 16 MHz -> Fbus == 4 Mh => CPUSPEED==2
int MONDATA = 0x48; // Flashing routines parameter block address
int MONRTN = 0xFE20; // Monitor mode return jump address
int EADDR = 0xFF7E; // For FLBPR in Flash the mass erase must use FLBPR as the
// erase address
// these are calculated
int DATABUF; // Flashing routines data buffer address (==MONDATA+4)
int PAGESIZE; // Databuffer size
int WORKRAM; // Work storage needed for calling flashing routines
int WORKTOP; // Topmost work storage address
int CTRLBYT; // Address of flashing routine control variable (==MONDATA+0)
int CPUSPD; // Address of flashing routine cpu speed variable (==MONDATA+1)
int LADDR; // Address of flashing routine last address variable (==MONDATA+2)
// HC908GZ16 Memory usage (note for some HC908 variants the ROM routines use
// memory starting from 0x80): 0x40 - 0x47 reserved for future ROM routine
// expansion needs 0x48 - 0x4B ROM routine parameters 0x4C - 0x6C ROM routine
// data buffer (64 bytes as used in this code) 0xAC - 0xFF Working storage for
// calling the ROM routines (about 17 bytes used)
// Cavets
// DATABUF size
// security erase at FLBPR, power cycle
int tickP1 = 15;
int tickP2 = 1023;
int verbose = 1;
int size = sizeof(image);
int useStdin = 0;
int dumpStart = 0;
int dumpSize = 0;
char* dumpFormat = "hex";
int eraseFlash = 0;
int verify = 0;
int baudRate = B9600;
char* executeCode = NULL;
int pageErase = 0;
int uploadOnly = 0;
int terminalMode = 0;
int connected = 0;
int useFastProg = 0;
int resetPulse = 0;
int killPrevious = 0;
int loadOnly = 0;
void comErr(char* fmt, ...) {
char buf[500];
va_list va;
va_start(va, fmt);
vsnprintf(buf, sizeof(buf), fmt, va);
fprintf(stderr, "%s", buf);
perror(COM);
va_end(va);
abort();
}
void flsprintf(FILE* f, char* fmt, ...) {
char buf[500];
va_list va;
va_start(va, fmt);
vsnprintf(buf, sizeof(buf), fmt, va);
fprintf(f, "%s", buf);
fflush(f);
va_end(va);
}
void initSerialPort() {
com = open(COM, O_RDWR | O_NOCTTY | O_NDELAY);
if (com < 0) comErr("Failed to open seria port");
fcntl(com, F_SETFL, 0);
struct termios opts;
tcgetattr(com, &opts);
opts.c_lflag &= ~(ICANON | ECHO | ECHOE | ISIG);
opts.c_cflag |= (CLOCAL | CREAD);
opts.c_cflag &= ~PARENB;
opts.c_cflag |= CSTOPB; // two stop bits
opts.c_cflag &= ~CSIZE;
opts.c_cflag |= CS8;
opts.c_oflag &= ~OPOST;
opts.c_iflag &= ~INPCK;
opts.c_iflag &= ~(IXON | IXOFF | IXANY);
opts.c_cc[VMIN] = 0;
opts.c_cc[VTIME] = 10; // 0.1 sec
cfsetispeed(&opts, baudRate);
cfsetospeed(&opts, baudRate);
if (tcsetattr(com, TCSANOW, &opts) != 0) {
perror(COM);
abort();
}
tcflush(com, TCIOFLUSH); // just in case some crap is the buffers
char buf = -2;
while (read(com, &buf, 1) > 0) {
if (verbose)
printf("Unexpected data from serial port: %02X\n", buf & 0xFF);
}
}
void putByte(int byte) {
char buf = byte;
if (verbose > 3) flsprintf(stdout, "TX: 0x%02X\n", byte);
int n = write(com, &buf, 1);
if (n != 1)
comErr("Serial port failed to send a byte, write returned %d\n", n);
}
int getByte() {
char buf;
int n = read(com, &buf, 1);
if (verbose > 3)
flsprintf(stdout, n < 1 ? "RX: fail\n" : "RX: 0x%02X\n", buf & 0xFF);
if (n == 1) return buf & 0xFF;
comErr("Serial port failed to receive a byte, read returned %d\n", n);
return -1; // never reached
}
// This reads away break 'character' from the serial line
void flushBreak() {
int i;
for (i = 0; i < 2; ++i) {
char buf;
int n = read(com, &buf, 1);
if (verbose > 3)
flsprintf(stdout, n < 1 ? "FL: nothing\n" : "FL: 0x%02X\n",
buf & 0xFF);
}
}
void sendByte(int byte) {
byte &= 0xFF;
putByte(byte);
char buf;
if (read(com, &buf, 1) != 1)
comErr("Loopback failed, nothing was received\n");
int rx = buf & 0xFF;
// if (byte != rx) {
// read(com, &buf, 1);
// comErr("Loopback failed, sent 0x%02X, got 0x%02X\n", byte, rx);
//}
rx = getByte();
// if (byte != rx)
// comErr("Target echo failed, sent 0x%02X, got 0x%02X\n", byte, rx);
}
void readMemory(int addr, int n, int tick) {
if (verbose > 2)
flsprintf(stdout, "Read memory address %04X size %04X\n", addr, n);
unsigned char* p = &image[addr];
sendByte(0x4A); // Monitor mode READ command
sendByte(addr >> 8);
sendByte(addr & 0xFF);
*(p++) = getByte();
n--;
int tc = 0;
while (n > 0) {
sendByte(0x1A); // Monitor mode IREAD command
int b1 = getByte();
int b2 = getByte();
*(p++) = b1;
n--;
if (n > 0) *(p++) = b2;
n--;
if (tick) {
tc++;
if ((tc & tickP1) == 0) flsprintf(stdout, ".");
// if ((tc & tickP2)==0)
// flsprintf(stdout,"\n");
}
}
}
void writeMemory(int addr, int n, int tick) {
if (verbose > 2)
flsprintf(stdout, "Write memory address %04X size %04X\n", addr, n);
unsigned char* p = &image[addr];
sendByte(0x49); // Monitor mode WRITE command
sendByte(addr >> 8);
sendByte(addr & 0xFF);
sendByte(*(p++));
int tc = 1;
while (n > 1) {
sendByte(0x19); // Monitor mode IWRITE command
sendByte(*(p++));
n -= 1;
if (tick) {
tc++;
if ((tc & tickP1) == 0) flsprintf(stdout, ".");
// if ((tc & tickP2)==0)
// flsprintf(stdout,"\n");
}
}
}
void connectTarget() {
int j;
if (connected) return;
// Hmmm, following does not work, how should we do this
// For blank device we need to send FF, but for non blank something else,
// oth reprogramming a non blank device, does it make sense
for (j = 0; j < 8; ++j) sendByte(0xFF);
flushBreak();
readMemory(0x40, 1, 0);
connected = 1;
if ((image[0x40] & 0x40) == 0)
flsprintf(stdout, "Failed to unlock the security");
// in case FLBPR is RAM based we clear it first by just writing it
image[FLBPR] = 0xFF;
writeMemory(FLBPR, 1, 0);
}
void dumpMemory(int addr, int n) {
unsigned char* p = &image[addr];
int i;
for (i = 0; i < n; ++i) {
if ((i & 0xF) == 0) flsprintf(stdout, "%04X ", addr + i);
flsprintf(stdout, "%02X ", *(p++) & 0xFF);
if ((i & 0xF) == 7) flsprintf(stdout, " ");
if ((i & 0xF) == 15) flsprintf(stdout, "\n");
}
if ((i & 0xF) != 0) flsprintf(stdout, "\n");
}
void dumpMemorySrec(int addr, int size) {
unsigned char* p = &image[addr];
while (size > 0) {
int n = size > 16 ? 16 : size;
int bc = 2 + n + 1;
flsprintf(stdout, "S1%02X%04X", bc, addr);
int s = (addr >> 8) + (addr & 0xFF) + bc;
int i;
for (i = 0; i < n; ++i) {
int bty = *p & 0xFF;
s += bty;
flsprintf(stdout, "%02X", bty);
++p;
++addr;
}
size -= n;
flsprintf(stdout, "%02X\n", ~s & 0xFF);
}
}
int readSP() {
if (verbose > 2) flsprintf(stdout, "Read Stak Pointer\n");
sendByte(0x0C); // Monitor mode READSP command
return (((getByte() << 8) | (getByte() & 0xFF)) - 1) & 0xFFFF;
}
int runFrom(int PC, int A, int CC, int HX) {
int SP = readSP();
if (verbose > 2)
flsprintf(stdout,
"Execute code PC=%04X A=%02X CC=%02X H:X=%04X SP=%04X\n", PC,
A, CC, HX, SP);
image[SP + 1] = HX >> 8;
image[SP + 2] = CC;
image[SP + 3] = A;
image[SP + 4] = HX & 0xFF;
image[SP + 5] = PC >> 8;
image[SP + 6] = PC & 0xFF;
writeMemory(SP + 1, 6, 0);
sendByte(0x28); // Monitor mode RUN command
return SP;
}
int lastMon = -1;
int callMonitor(int mon, int ctrlbyt, int accu, int faddr, int laddr) {
int SP = readSP();
image[CTRLBYT] = ctrlbyt; // CTRLBYT BIT 6 = 1 = > mass erase
image[CPUSPD] =
CPUSPEED; // CPUSPD = 16 MHz ext clock = > 4 MHz Fbus speed = > 8
image[LADDR] = laddr >> 8;
image[LADDR + 1] = laddr & 0xFF;
writeMemory(MONDATA, 4, 0);
if (WORKRAM > 0xFF) {
flsprintf(stderr, "Work RAM must be on zero page");
abort();
}
if (lastMon != mon) {
// construct small HC908 code fragment to call the monitor function and
// return results
int i = WORKRAM + 2;
image[i++] = 0xCD; // JSR mon ; calls the monitor routine
image[i++] = mon >> 8;
image[i++] = mon & 0xFF;
image[i++] = 0x87; // PSHA ; save condition A
image[i++] = 0x85; // TPA ; condition codes to A
image[i++] = 0xB7; // STA ; store to WORKRAM
image[i++] = WORKRAM;
image[i++] = 0x86; // PULA ; restore A
image[i++] = 0xB7; // STA ; store to WORKRAM+1
image[i++] = WORKRAM + 1;
image[i++] = 0x45; // LDHX #SP+1 ; restore stack pointer
image[i++] = (SP + 1) >> 8;
image[i++] = (SP + 1) & 0xFF;
image[i++] = 0x94; // TXS
image[i++] = 0xCC; // JMP back to MON (this is the only way)
image[i++] = MONRTN >> 8;
image[i++] = MONRTN & 0xFF;
if (WORKRAM >=
WORKTOP) { // leave some stack space for monitor routines
flsprintf(stderr, "Not enough WORKRAM on target");
abort();
}
writeMemory(WORKRAM, i - WORKRAM, 0);
lastMon = mon;
}
// now execute the fragment
runFrom(WORKRAM + 2, accu, 0x00, faddr);
// fetch the return values
readMemory(WORKRAM, 2, 0);
return ((image[WORKRAM] & 0xFF) << 8) |
(image[WORKRAM + 1] & 0xFF); // return condition codes and accu
}
int fastProg(int faddr, int n) {
static int n_addr;
static int last_n;
if (WORKRAM > 0xFF) {
flsprintf(stderr, "Work RAM must be on zero page");
abort();
}
if (lastMon != -1) {
int SP = readSP();
image[CTRLBYT] = 0; // CTRLBYT =0
image[CPUSPD] = CPUSPEED; // CPUSPD = 16 MHz ext clock = > 4 MHz
// Fbus speed = > 8
// construct small HC908 code fragment to call the monitor function and
// return results
int i = WORKRAM;
image[i++] = 0x9D; // NOP / reserve space
image[i++] = 0x9D; // NOP / reserve space
image[i++] = 0x35; // STHX WORKRAM (dir)
image[i++] = WORKRAM;
image[i++] = 0x45; // LDHX #DATABUF
image[i++] = DATABUF >> 8;
image[i++] = DATABUF & 0xFF;
image[i++] = 0xCD; // JSR GETBYTE ; calls the monitor routine
image[i++] = GETBYTE >> 8;
image[i++] = GETBYTE & 0xFF;
// image[ i++ ] = 0xFE9C>>8; ; // 0xFE9C = GET WITH echo
// image[ i++ ] = 0xFE9C&0xFF;
image[i++] = 0xF7; // STA ,X
image[i++] = 0x5C; // INCX
image[i++] = 0xA3; // cpx #DATABUF+n
n_addr = i;
image[i++] = 0; // place holder
image[i++] = 0x25; // BLO *-10
image[i++] = (-9) & 0xFF;
image[i++] = 0x55;
image[i++] = WORKRAM;
image[i++] = 0xCD; // JSR PRGRNGE ; calls the monitor routine
image[i++] = PRGRNGE >> 8;
image[i++] = PRGRNGE & 0xFF;
image[i++] = 0x45; // LDHX #SP+1 ; restore stack pointer
image[i++] = (SP + 1) >> 8;
image[i++] = (SP + 1) & 0xFF;
image[i++] = 0x94; // TXS
image[i++] = 0xCC; // JMP back to MON (this is the only way)
image[i++] = MONRTN >> 8;
image[i++] = MONRTN & 0xFF;
if (WORKRAM >=
WORKTOP) { // leave some stack space for monitor routines
flsprintf(stderr, "Not enough WORKRAM on target");
abort();
}
writeMemory(WORKRAM, i - WORKRAM, 0);
// dumpMemorySrec(WORKRAM , i-WORKRAM);
lastMon = -1;
}
if (last_n != n) {
image[n_addr] = DATABUF + n;
writeMemory(n_addr, 1, 0);
last_n = n;
}
int laddr = faddr + n - 1;
image[LADDR] = laddr >> 8;
image[LADDR + 1] = laddr & 0xFF;
writeMemory(LADDR, 2, 0);
// now execute the fragment
runFrom(WORKRAM + 2, 0x00, 0x00,
faddr); // NOTE! to override flash security and erase FLBPR at be
// used as the erase address for mass erase
int i;
for (i = 0; i < n; ++i) putByte(image[faddr + i]);
for (i = 0; i < n; ++i) getByte();
readSP();
return 0;
}
int fastProg2(int faddr, int progn) {
if (WORKRAM > 0xFF) {
flsprintf(stderr, "Work RAM must be on zero page");
abort();
}
int SP = readSP();
image[CTRLBYT] = 0; // CTRLBYT =0
image[CPUSPD] = CPUSPEED; // CPUSPD= 16 MHz ext => 4 MHz Fbus = > 8
// construct small HC908 code fragment to call the monitor function
int i = WORKRAM;
int j;
int FADDR = i;
image[i++] = faddr >> 8; // FADDR initial contents
image[i++] = faddr & 0xFF;
int PROGN = i;
image[i++] = progn >> 8; // PROGN initial contents
image[i++] = progn & 0xFF;
int start = i;
if (lastMon != -1) {
image[i++] = 0x55; // LDHX PROGN get bytes left to program
image[i++] = PROGN;
image[i++] = 0x27; // BEQ DONE branch if all done
int patchDone = i;
image[i++] = 0;
image[i++] = 0x65; // CPHX #PAGESIZE only page full at a time
image[i++] = 0;
image[i++] = PAGESIZE;
image[i++] = 0x25; // BLO DOIT
image[i++] = 2;
image[i++] = 0xAE; // LDX #PAGESIZE
image[i++] = PAGESIZE;
image[i++] = 0x9F; // TXA
image[i++] = 0x87; // PSHA
image[i++] = 0xB6; // LDA PROGN+1
image[i++] = PROGN + 1;
image[i++] = 0x9E; // SUB 1,SP
image[i++] = 0xE0;
image[i++] = 1;
image[i++] = 0xB7; // STA PROGN+1
image[i++] = PROGN + 1;
image[i++] = 0xB6; // LDA PROGN
image[i++] = PROGN;
image[i++] = 0xA2; // SBC #0
image[i++] = 0;
image[i++] = 0xB7; // STA PROGN
image[i++] = PROGN;
image[i++] = 0x9F; // TXA
image[i++] = 0x4A; // DECA
image[i++] = 0xBB; // ADD FADDR+1
image[i++] = FADDR + 1;
image[i++] = 0xB7; // STA LADDR+1
image[i++] = LADDR + 1;
image[i++] = 0xB6; // LDA FADDR
image[i++] = FADDR;
image[i++] = 0xA9; // ADC #0
image[i++] = 0;
image[i++] = 0xB7; // STA LADDR
image[i++] = LADDR;
image[i++] = 0x45; // LDHX #DATABUF
image[i++] = DATABUF >> 8;
image[i++] = DATABUF & 0xFF;
image[i++] = 0x86; // PULA
int getMore = i;
image[i++] = 0x87; // PSHA
image[i++] = 0xCD; // JSR GETBYTE
image[i++] = GETBYTE >> 8;
image[i++] = GETBYTE & 0xFF;
image[i++] = 0xF7; // STA ,X
image[i++] = 0x5C; // INCX
image[i++] = 0x86; // PULA
image[i++] = 0x4A; // DECA
image[i++] = 0x26; // BNE GETMORE
j = i;
image[i++] = (getMore - (j + 1)) & 0xFF;
image[i++] = 0x55; // LDHX FADDR
image[i++] = FADDR;
image[i++] = 0xCD; // JSR PRGRNGE
image[i++] = PRGRNGE >> 8;
image[i++] = PRGRNGE & 0xFF;
image[i++] = 0x8B; // PSHH Annoyingly JB8 RDVRNG leaves H:X off by one
image[i++] =
0x89; // PSHX compared to GZ16 necessiating this pus/pul sequence
image[i++] = 0x55; // LDHX FADDR
image[i++] = FADDR;
image[i++] = 0xA6; // LDAA #1
image[i++] = 1;
image[i++] = 0xCD; // JSR RDVRRNG
image[i++] = RDVRRNG >> 8;
image[i++] = RDVRRNG & 0xFF;
image[i++] = 0x88; // PULX
image[i++] = 0x8A; // PULH
image[i++] = 0x35; // STHX FADDR
image[i++] = FADDR;
image[i++] = 0xCD; // JSR PUTBYTE
image[i++] = PUTBYTE >> 8;
image[i++] = PUTBYTE & 0xFF;
image[i++] = 0x20; // BRA start
j = i;
image[i++] = (start - (j + 1)) & 0xFF;
image[patchDone] = (i - (patchDone + 1)) & 0xFF;
image[i++] = 0x45; // LDHX #SP+1 ; restore stack pointer
image[i++] = (SP + 1) >> 8;
image[i++] = (SP + 1) & 0xFF;
image[i++] = 0x94; // TXS
image[i++] = 0xCC; // JMP back to MON (this is the only way)
image[i++] = MONRTN >> 8;
image[i++] = MONRTN & 0xFF;
if (i >= WORKTOP) { // leave some stack space for monitor routines
flsprintf(stderr, "Not enough WORKRAM on target");
abort();
}
writeMemory(WORKRAM, i - WORKRAM, 0);
lastMon = -1;
} else
writeMemory(WORKRAM, 4, 0);
runFrom(start, 0x00, 0x00, 0);
while (progn) {
int n = progn < PAGESIZE ? progn : PAGESIZE;
int sum = 0;
for (i = 0; i < n; ++i) {
putByte(image[faddr + i]);
}
for (i = 0; i < n; ++i) {
int b = getByte();
// flsprintf(stderr,"%d %02X\n",i,b);
sum = (sum + b) & 0xFF;
if (b != image[faddr + i])
flsprintf(
stderr,
"Program data transfer error, at %04X sent %02X got %02\n",
faddr + i, image[faddr + i], b);
}
int back = getByte();
// flsprintf(stderr,"%02X\n",back);
if (back != sum)
flsprintf(stderr,
"Program checksum failure, at %04X size %04X checksum "
"calculated %02X received %02X\n",
faddr, n, sum, back);
progn -= n;
faddr += n;
if (verbose) flsprintf(stdout, ".");
}
return 0;
}
void massErase() {
// NOTE! to override flash security and erase FLBPR must be used as the
// erase address for mass erase
if (verbose) flsprintf(stdout, "Mass erase\n");
callMonitor(ERARRNG, 0x40, 0, EADDR, 0);
}
void flashProgram(int addr, int size, int verify) {
if (addr < FLASH) {
flsprintf(stdout,
"Programming address %04X below flash start address %04X\n",
addr, FLASH);
exit(0);
}
if (useFastProg) {
if (verbose)
flsprintf(stdout, "Program %04X - %04X ", addr, addr + size - 1);
fastProg2(addr, size);
if (verbose) flsprintf(stdout, "\n");
} else {
while (size > 0) {
int n = size <= PAGESIZE ? size : PAGESIZE;
if (verbose)
flsprintf(stdout, "Program %04X - %04X ", addr, addr + n - 1);
memcpy(&image[DATABUF], &image[addr], n);
writeMemory(DATABUF, n, verbose);
callMonitor(PRGRNGE, 0, 0, addr, addr + n - 1);
if (verify) {
int cca = callMonitor(RDVRRNG, 0, 1, addr, addr + n - 1);
int sum = 0;
int i;
for (i = 0; i < n; ++i) sum = (sum + image[addr + i]) & 0xFF;
int back = cca & 0xFF; // get Accu from target
if (sum != back) {
flsprintf(stderr,
"Program checksum failure, at %04X size %04X "
"checksum calculated %02X received %02\n",
addr, n, sum, back);
abort();
}
if (!(cca & 0x0100)) { // check Carry bit from target
flsprintf(stderr, "Verify failed\n");
abort();
}
if (verbose) flsprintf(stdout, "OK");
}
if (verbose) flsprintf(stdout, "\n");
addr += n;
size -= n;
}
}
}
int readSrec(int verbose, FILE* sf, unsigned char* image, int size, int base,
int* ranges, int rn) {
if (verbose) flsprintf(stdout, "Reading S-records\n");
memset(image, 0xff, size);
char line[2 + 2 + 255 * 2 + 2 +
1]; // Sx + count + 255 bytes for data address & checksum + CR/LF
// +nul (in windows)
int amax = 0;
int rc = 0;
while (fgets(line, sizeof(line), sf) != NULL) {
int o = 0;
if (line[0] == 'S') {
unsigned int n, a;
sscanf(line + 2, "%2x", &n);
n--;
if (line[1] == '1') {
sscanf(line + 4, "%4x", &a);
n = n - 2;
o = 8;
}
if (line[1] == '2') {
sscanf(line + 4, "%6x", &a);
n = n - 4;
o = 10;
}
if (line[1] == '3') {
sscanf(line + 4, "%8x", &a);
n = n - 6;
o = 12;
}
if (o != 0) {
int i, j;
if (ranges) {
for (i = 0; i < rc; i += 2) {
int rlo = ranges[i];
int rhi = rlo + ranges[i + 1];
if (!((a + n <= rlo) || (rhi <= a))) {
flsprintf(stderr,
"Overlapping S-record ranges %04X,%04X "
"and %0x4 %04X\n",
rlo, rhi - rlo, a, n);
abort();
}
}
if (rc + 2 >= rn) return -1;
ranges[rc] = a;
ranges[rc + 1] = n;
rc += 2;
int cf = 0;
do
compact : {
for (i = 0; i < rc; i += 2) {
for (j = i + 2; j < rc; j += 2) {
cf = 1;
if (ranges[i] + ranges[i + 1] == ranges[j])
ranges[i + 1] += ranges[j + 1];
else if (ranges[i] == ranges[j] + ranges[j + 1])
ranges[i] -= ranges[j + 1];
else
cf = 0;
if (cf) {
for (i = j + 2; i < rc; i++)
ranges[i] = ranges[i + 2];
rc -= 2;
cf = 0;
goto compact;
}
}
}
}
while (cf)
;
}
for (i = 0; i < n; ++i) {
unsigned int d;
sscanf(line + o + i * 2, "%2x", &d);
if ((a >= base) && (a < base + size)) {
image[a - base] = d;
a++;
amax = a > amax ? a : amax;
}
}
}
}
if (verbose > 1) flsprintf(stdout, ">>> %s", line);
if (verbose && o == 0) flsprintf(stdout, "Line ignored: %s\n", line);
}
if (verbose) {
if (ranges) {
int i;
for (i = 0; i < rc; i += 2)
flsprintf(stdout, "S-record data address %06X size %06X\n",
ranges[i], ranges[i + 1]);
}
flsprintf(stdout, "\n");
}
return rc;
}
void printHelp() {
flsprintf(stdout,
"bl08 burns MC68HC908 Flash memory from S-record file(s) using "
"Monitor mode\n");
flsprintf(stdout, "Usage: \n");
flsprintf(stdout, " bl08 [-abcdefhiklmnpqrstuvx] [filename...]\n");
flsprintf(
stdout,
" -a address Set dump memory address (needs -s option too)\n");
flsprintf(stdout,
" -b baudrate Set baudrate for target communication\n");
flsprintf(stdout,
" -c device Set serial com device used to communicate with "
"target\n");
flsprintf(stdout, " typically /dev/ttyS0 \n");
flsprintf(
stdout,
" -d dumpformat Set dump format, supported formats are: 'srec'\n");
flsprintf(stdout,
" -e Erase target using mass erase mode, clearing "
"security bytes\n");
flsprintf(stdout, " -f Use fast programming method");
flsprintf(stdout,
" -g address Go execute target code from address or use '-g "
"reset'\n");
flsprintf(stdout, " -h Print out this help text\n");
flsprintf(stdout,
" -i Read input (S-records) from standard inputi\n");
flsprintf(stdout, " -k Kill previous instance of bl08\n");
flsprintf(stdout,
" -l verbosity Set verbosity level, valid values are 0-4, "
"default 1\n");
flsprintf(stdout, " -m Terminal emulator mode\n");
flsprintf(stdout, " -n Print bl08 version number\n");
flsprintf(stdout, " -o param=value Override target parameter value\n");
flsprintf(stdout,
" param = "
"ROMBASE,FLASH,PUTBYTE,GETBYTE,RDVRRNG,MONRTN\n");
flsprintf(stdout,
" "
"ERARRNG,PRGRNGE,FLBPR,MONDATA,PAGESIZE,EADDR\n");
flsprintf(stdout,
" -p Use page erase when programming flash\n");
flsprintf(stdout, " -q Run quietly, same as -l 0\n");
flsprintf(stdout, " -s size Set dump memory size\n");
flsprintf(stdout, " -r pulse Pulse DTR for pulse milliseconds\n");
flsprintf(stdout,
" -t cputype Set CPU type, valid values are: 'gz16'\n");
flsprintf(stdout, " -u Upload only (do not program flash)\n");
flsprintf(stdout, " -v Verify when programming \n");
flsprintf(stdout,
" -x cpuspeed Set CPU speed, typically set for Fbus (in MHz) "
"x 4 \n");
flsprintf(stdout,
" -z Do not program, do no upload, just read in the "
"S-rec file \n");
flsprintf(stdout,
" addresses and sizes in decimal, for hex prefix with '0x'\n");
// Example
exit(0);
}
void termEmu() {
int STDIN = 0;
int STDOUT = 1;
// get rid of stuff that has been echoed to us
tcflush(com, TCIFLUSH); // .. then get rid of the echoes and what ever...
struct termios newtio, oldtio;
tcgetattr(STDIN, &oldtio); // we do not want to change the console setting
// forever, so keep the old
tcgetattr(STDIN, &newtio); // configure the console to return as soon as it
// a key is pressed
newtio.c_lflag = 0;
newtio.c_cc[VTIME] = 0;
newtio.c_cc[VMIN] = 1;
tcsetattr(STDIN, TCSANOW, &newtio);
flsprintf(stdout, "\nTerminal mode, press CTRL-C to exit.\n");
fd_set readfs;
int maxfd;
int res;
char buf[2];
maxfd = com + 1;
while (1) {
// wait for something from console or serial port
FD_SET(STDIN, &readfs);
FD_SET(com, &readfs);
struct timeval tout;
tout.tv_usec = 10;
tout.tv_sec = 0;
select(maxfd, &readfs, NULL, NULL, &tout);
// copy console stuff to the serial port
if (FD_ISSET(STDIN, &readfs)) {
res = read(STDIN, buf, 1);
if (buf[0] == 3) break; // CTRL-C terminates terminal mode
write(com, buf, res);
}
// copy serial port stuff to console
if (FD_ISSET(com, &readfs)) {
res = read(com, buf, 1);
write(STDOUT, buf, res);
fflush(stdout);
}
}
tcsetattr(0, TCSANOW, &oldtio); // restore console settings
/// close(con);
}
void setCPUtype(char* cpu) {
if (strcmp("gz16", cpu) == 0) {
// These settings depend on the CPU version
FLASH = 0xC000;
PUTBYTE = 0xFEAF;
GETBYTE = 0x1C00;
RDVRRNG = 0x1C03;
ERARRNG = 0x1C06;
PRGRNGE = 0x1C09;
MONRTN = 0xFE20;
FLBPR = 0xFF7E;
EADDR = FLBPR;
MONDATA = 0x48;
PAGESIZE = 64;
} else if (strcmp("jb8", cpu) == 0) {
// These settings depend on the CPU version
FLASH = 0xDC00;
PUTBYTE = 0xFED6;
GETBYTE = 0xFC00;
RDVRRNG = 0xFC03;
ERARRNG = 0xFC06;
PRGRNGE = 0xFC09;
MONRTN = 0xFE55;
FLBPR = 0xFE09;
EADDR = FLASH;
MONDATA = 0x48;
PAGESIZE = 64;
} else {
flsprintf(stderr, "Unsupported CPU type '%s'\n", cpu);
abort();
}
// these are independent of CPU type
DATABUF = MONDATA + 4;
WORKRAM = DATABUF + PAGESIZE;
WORKTOP = 0xF0; // this leaves 16 bytes for stack, the deepest ROM routines
// use 11 so there some for myself too
CTRLBYT = MONDATA + 0;
CPUSPD = MONDATA + 1;
LADDR = MONDATA + 2;
if (DATABUF + PAGESIZE > 0xFF) {
flsprintf(stderr,
"bl08 limitation, DATABUF+PAGESIZE>0xFF, DATABUF=%04X, "
"PAGESIZE=%04X\n",
DATABUF, PAGESIZE);
abort();
}
}
int getIntArg(char* arg) {
if (strlen(arg) >= 2 && memcmp(arg, "0x", 2) == 0) {
unsigned int u;
sscanf(arg + 2, "%X", &u);
return u;
} else {
int d;
sscanf(arg, "%d", &d);
return d;
}
}
void parseOverride(char* str) {
char* vp = strstr(str, "=");
if (!vp) {
flsprintf(stderr, "Bad override syntax, no '=' found\n");
abort();
}
*vp = 0;
vp++;
int val = getIntArg(vp);
if (strcmp("ROMBASE", str) == 0) {
GETBYTE = val + 0;
RDVRRNG = val + 3;
ERARRNG = val + 6;
PRGRNGE = val + 9;
} else if (strcmp("FLASH", str) == 0)
FLASH = val;
else if (strcmp("PUTBYTE", str) == 0)
PUTBYTE = val;
else if (strcmp("GETBYTE", str) == 0)
GETBYTE = val;
else if (strcmp("RDVRRNG", str) == 0)
RDVRRNG = val;
else if (strcmp("ERARRNG", str) == 0)
ERARRNG = val;
else if (strcmp("PRGRNGE", str) == 0)
PRGRNGE = val;
else if (strcmp("FLBPR", str) == 0)
FLBPR = val;
else if (strcmp("MONDATA", str) == 0)
MONDATA = val;
else if (strcmp("PAGESIZE", str) == 0)
PAGESIZE = val;
else if (strcmp("MONRTN", str) == 0)
MONRTN = val;
else if (strcmp("EADDR", str) == 0)
EADDR = val;
else {
flsprintf(stderr, "Attempt to override unrecognized variable %s\n",
str);
abort();
}
}
void parseArgs(int argc, char* argv[]) {
int c;
while ((c = getopt(argc, argv, "a:b:c:d:efg:hikl:mno:pqr:s:t:uvx:z")) !=
-1) {
switch (c) {
case 'a':
dumpStart = getIntArg(optarg);
break;
case 'b':
sscanf(optarg, "%d", &baudRate);
break;
case 'c':
COM = optarg;
break;
case 'd':
dumpFormat = optarg;
break;
case 'e':
eraseFlash = 1;
break;
case 'f':
useFastProg = 1;
break;
case 'g':
executeCode = optarg;
break;
case 'h':
printHelp();
break;
case 'i':
useStdin = 1;
break;
case 'k':
killPrevious = 1;
break;
case 'l':
sscanf(optarg, "%d", &verbose);
break;
case 'm':
terminalMode = 1;
break;
case 'n':
flsprintf(stdout, "%s\n", version);
break;
case 'p':
pageErase = 1;
break;
case 'o':
parseOverride(optarg);
break;
case 'q':
verbose = 0;
break;
case 'r':
resetPulse = getIntArg(optarg);
break;
case 's':
dumpSize = getIntArg(optarg);
break;
case 't':
setCPUtype(optarg);
break;
case 'u':
uploadOnly = 1;
break;
case 'v':
verify = 1;
break;
case 'x':
sscanf(optarg, "%d", &CPUSPEED);
break;
case 'z':
loadOnly = 1;
break;
case '?':
if (isprint(optopt))
fprintf(stderr, "Unknown option `-%c'.\n", optopt);
else
fprintf(stderr, "Unknown option character `\\x%x'.\n",
optopt);
default:
fprintf(stderr, "Bug, unhandled option '%c'\n", c);
abort();
}
}
if (argc <= 1) printHelp();
}
void ioctlErrCheck(int e) {
if (e) {
flsprintf(stdout, "ioctl returned %d\n", e);
abort();
}
}
void generateReset() {
#define TIOCM_DTR 0x002
int s;
ioctlErrCheck(ioctl(com, TIOCMGET, &s));
s |= TIOCM_DTR;
ioctlErrCheck(ioctl(com, TIOCMSET, &s));
struct timespec tspec;
tspec.tv_sec = resetPulse / 1000;
tspec.tv_nsec = (resetPulse % 1000) * 1000000;
nanosleep(&tspec, 0);
s &= ~TIOCM_DTR;
ioctlErrCheck(ioctl(com, TIOCMSET, &s));
}
void deletePidFile() {
int stat = remove(PIDFILENAME);
if (stat) printf("remove returned %d\n", stat);
}
void killPreviousInstance() {
atexit(deletePidFile);
int pid;
FILE* pidf = fopen(PIDFILENAME, "r");
if (pidf) {
fscanf(pidf, "%d", &pid);
int stat = kill(pid, SIGKILL);
if (stat != 0) printf("kill returned %d\n", stat);
fclose(pidf);
waitpid(pid, &stat, 0);
if (WIFEXITED(stat) == 0)
printf("waitpid returned %d\n", WIFEXITED(stat));
}
pidf = fopen(PIDFILENAME, "w");
fprintf(pidf, "%d\n", getpid());
fclose(pidf);
}
int main(int argc, char* argv[]) {
// default values
setCPUtype("gz16");
CPUSPEED = 8;
baudRate = 14400;
parseArgs(argc, argv);
if (killPrevious) killPreviousInstance();
if (verbose)
flsprintf(stdout, "bl08 - MC68HC908 Bootloader - version %s\n",
version);
memset(image, 0xFF, sizeof(image));
int maxrc = 256;
int ranges[maxrc];
int rc = 0;
if (useStdin)
rc += readSrec(verbose, stdin, image, sizeof(image), 0x0000,
ranges + rc, maxrc - rc);
int i;
for (i = optind; i < argc; i++) {
char* filename = argv[i];
FILE* sf = fopen(filename, "r");
if (sf == NULL) {
flsprintf(stderr, "Failed to open '%s'\n", filename);
abort();
}
int rn = readSrec(verbose, sf, image, sizeof(image), 0x0000,
ranges + rc, maxrc - rc);
if (rn < 0) {
flsprintf(
stderr,
"Too many discontinuous data ranges in S-record file '%s'\n",
filename);
abort();
}
rc += rn;
fclose(sf);
}
if (!loadOnly) {
initSerialPort();
if (resetPulse) generateReset();
if (eraseFlash) {
connectTarget();
massErase();
}
if (rc > 0) {
connectTarget();
if (pageErase) {
for (i = 0; i < rc;) {
int addr = ranges[i++];
int size = ranges[i++];
int a = addr / PAGESIZE * PAGESIZE;
while (a < addr + size) {
if (verbose)
flsprintf(stdout, "Erase %04X - %04X\n", a,
PAGESIZE);
callMonitor(ERARRNG, 0, 0, a, 0);
a += PAGESIZE;
}
}
} else
massErase();
int i;
for (i = 0; i < rc;) {
int addr = ranges[i++];
int size = ranges[i++];
if (uploadOnly) {
if (verbose)
flsprintf(stdout, "Uploading memory contents\n");
writeMemory(addr, size, verbose);
if (verbose) flsprintf(stdout, "\n");
} else
flashProgram(addr, size, verify);
if (verbose > 1) {
flsprintf(stdout, "Reading back memory/flash content");
readMemory(addr, size, verbose);
flsprintf(stdout, "\n");
dumpMemory(addr, size);
}
}
}
if (executeCode) {
connectTarget();
int addr;
if (strcmp("reset", executeCode) == 0) {
readMemory(0xFFFE, 2, 0);
addr = ((image[0xFFFE] & 0xFF) << 8) | (image[0xFFFF] & 0xFF);
} else
addr = getIntArg(executeCode);
if (verbose) flsprintf(stdout, "Execute code from %04X\n", addr);
runFrom(addr, 0, 0, 0);
}
if (terminalMode) termEmu();
}
if (dumpSize > 0) {
if (!loadOnly) {
connectTarget();
if (verbose) flsprintf(stdout, "Reading memory\n");
readMemory(dumpStart, dumpSize, verbose);
}
if (verbose) flsprintf(stdout, "\n");
if (dumpFormat) {
if (strcmp("srec", dumpFormat) == 0)
dumpMemorySrec(dumpStart, dumpSize);
else if (strcmp("hex", dumpFormat) == 0)
dumpMemory(dumpStart, dumpSize);
else
flsprintf(stderr, "Unknown dump format '%s'\n", dumpFormat);
}
}
return 0;
}