bl08.c - Free HC08 Bootloader Source Code

Try this link for a zip-archive of the source code.
/*
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 <stdlib.h>
#include <fcntl.h>
#include <termios.h>
#include <stdarg.h>
#include <ctype.h>
#include <stdio.h>
#include <unistd.h>
#include <time.h>
#include <string.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"

char* COM = "/dev/tty.usbserial-FTOXM3NX";
//char* COM = "/dev/ttyS0";

int com;

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=B14400;
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)
                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() {
        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;
        }








syntax highlighted by Code2HTML, v. 0.9.1