/* dnload.c ****************************************************************************** dnload.c - download S-record hexfile to 6811 MCU in boot mode Christopher Giese Release date 7/17/98. Distribute freely. ABSOLUTELY NO WARRANTY. Notes: Should support 68HC11 A-series but I have not tested this. Bugs/to do: - Support binary, Intel-hex, other file formats? - Add command-line option to download minimal number of bytes (instead of 256 bytes minimum needed by A-series). *****************************************************************************/ #include /* exit() */ #include /* strncpy() strupr() strstr() strcat() */ #include /* printf() */ #include /* kbhit() getch() cprintf() */ #include /* toupper() */ #include /* inportb() outportb() peek() delay() */ #if defined(__DJGPP__) #define strncmpi strncasecmp #define peek _farpeekw /* make the executable a little bit smaller. It also helps immensely if you run STRIP on the DJGPP executable. */ __crt0_load_environment_file() { } #endif #define BUF_LEN 256 #define CODE_LEN 1024 char Code[CODE_LEN]; /***************************************************************************** name: portExists action: interrogates BIOS data segment to see if a COM port exists returns:zero if the port (0-3) doesn't exist, I/O address if it does *****************************************************************************/ unsigned portExists(unsigned Port) { return(peek(0x40, 2 * Port)); } /***************************************************************************** name: portRead action: tries to read from COM port. If Timeout is nonzero, times out after Timeout ms. Also returns if a key is pressed. returns:-1 if timeout, -2 if keypress, else byte that was read *****************************************************************************/ int portRead(unsigned PortAdr, unsigned Timeout) { unsigned Temp; /* make sure PortAdr + 0 points to tx/rx register; not BRG register */ outportb(PortAdr + 3, inportb(PortAdr + 3) & 0x7F); for(Temp=Timeout ? Timeout : 1; Temp; ) { if(inportb(PortAdr + 5) & 1) return(inportb(PortAdr)); if(kbhit()) { getch(); return(-2); } delay(1); if(Timeout) Temp--; } return(-1); } /***************************************************************************** name: portWrite action: tries to write to the COM port. If Timeout is nonzero, times out after Timeout ms. Also returns if a key is pressed. returns:-1 if timeout, -2 if keypress, zero if sucess *****************************************************************************/ int portWrite(unsigned PortAdr, char Byte, unsigned Timeout) { unsigned Temp; /* make sure PortAdr + 0 points to tx/rx register; not BRG register */ outportb(PortAdr + 3, inportb(PortAdr + 3) & 0x7F); for(Temp=Timeout ? Timeout : 1; Temp; ) { if(inportb(PortAdr + 5) & 0x20)//& 0x40) { outportb(PortAdr, Byte); return(0); } if(kbhit()) { getch(); return(-2); } delay(1); if(Timeout) Temp--; } return(-1); } /***************************************************************************** name: getHexNybble action: reads character at String, converts it from hex ASCII to binary returns:converted binary nybble or -1 if bad hex digit *****************************************************************************/ int getHexNybble(char *String) { int RetVal; RetVal=toupper(*String); if(RetVal < '0' || RetVal > 'F') return(-1); if(RetVal > '9') { if(RetVal < 'A') return(-1); RetVal -= 7; } return(RetVal - '0'); } /***************************************************************************** name: getHexByte action: reads two characters at String, converts them from hex ASCII to binary returns:converted binary byte or -1 if bad hex digit(s) *****************************************************************************/ int getHexByte(char *String) { int Temp, RetVal=0; Temp=getHexNybble(String++); if(Temp < 0) return(Temp); RetVal=(RetVal << 4) | Temp; Temp=getHexNybble(String++); if(Temp < 0) return(Temp); RetVal=(RetVal << 4) | Temp; return(RetVal); } /***************************************************************************** name: getHexWord action: reads four characters at String, converts them from hex ASCII to binary returns:converted binary word (16-bit) or -1 if bad hex digit(s) *****************************************************************************/ long getHexWord(char *String) { long RetVal=0; int Temp; Temp=getHexNybble(String++); if(Temp < 0) return(Temp); RetVal=(RetVal << 4) | Temp; Temp=getHexNybble(String++); if(Temp < 0) return(Temp); RetVal=(RetVal << 4) | Temp; Temp=getHexNybble(String++); if(Temp < 0) return(Temp); RetVal=(RetVal << 4) | Temp; Temp=getHexNybble(String++); if(Temp < 0) return(Temp); RetVal=(RetVal << 4) | Temp; return(RetVal); } /***************************************************************************** name: main *****************************************************************************/ void main(int ArgCnt, char *ArgVal[]) { int PortAdr, RecLen, Offset, HiAdr, Adr, Temp; char Buffer[BUF_LEN]; FILE *Handle; if(ArgCnt < 2) USE: { printf("Downloads S-record hexfile to 6811 MCU via serial " "port. Usage:\nDNLOAD HEXFILE[.S19] [COMn]\n"); exit(1); } /* process filename */ strncpy(Buffer, ArgVal[1], BUF_LEN - 5); strupr(Buffer); if(strstr(Buffer, ".") == NULL) strcat(Buffer, ".S19"); /* process optional port name */ if(ArgCnt >= 3) { if(!strncmpi(ArgVal[2], "COM", 3)) { Temp=ArgVal[2][3]; if(Temp < '1' || Temp > '4') goto COM; Temp -= '1'; } else COM: { printf("Second argument must specify a valid COM " "port (e.g. 'COM1', 'com2', 'CoM4').\n"); exit(1); }} /* only two args? Set Temp=0 (i.e. use COM1) */ else Temp=0; /* detect COM port */ PortAdr=portExists(Temp); if(PortAdr == 0) { printf("Sorry, COM%1u does not exist on this PC.\n", Temp + 1); exit(2); } /* set port for 1200 N 8 1 */ outportb(PortAdr + 3, 0x80); outportb(PortAdr, 0x60); /* 1200 */ outportb(PortAdr + 1, 0); outportb(PortAdr + 3, 0x03); /* 8 N 1 */ /* open file */ Handle=fopen(Buffer, "r"); if(Handle == NULL) { printf("Can't open file '%s'.\n", Buffer); exit(3); } /* fill Code with 6811 NOPs */ memset(Code, 0x01, CODE_LEN); /* read file */ HiAdr=0; while(fgets(Buffer, BUF_LEN, Handle) != NULL) /* S9 marks end of file */ { if(!strncmpi(Buffer, "S9", 2)) break; /* S1 marks a record */ if(strncmpi(Buffer, "S1", 2)) continue; /* total bytes in this line, after the S1 */ if((RecLen=getHexByte(Buffer + 2)) < 0) BAD: { printf("Bad S-record:\n%s\n", Buffer); exit(4); } /* load address for this line */ if((Adr=getHexWord(Buffer + 4)) < 0) goto BAD; if(Adr >= CODE_LEN) { printf("Load address of S-record > %u:\n%s\n", CODE_LEN, Buffer); exit(4); } /* data bytes */ for(Offset=0; Offset < RecLen - 3; Offset++) { if((Temp=getHexByte(Buffer + 8 + 2 * Offset)) < 0) goto BAD; Code[Adr + Offset]=Temp; } /* ignore checksum */ if(Adr + Offset > HiAdr) HiAdr=Adr + Offset; } /* await break (zero byte) */ printf("Awaiting BREAK from 6811 system...\n"); Temp=portRead(PortAdr, 0); ERR: if(Temp == -1) { printf("Timeout while reading/writing serial port.\n"); exit(5); } else if(Temp == -2) { printf("Aborted...\n"); exit(6); } /* send 0xFF. This tells 6811 to switch from 7812 baud to 1200 baud */ printf("Sending 0xFF byte...\n"); Temp=portWrite(PortAdr, 0xFF, 1000); if(Temp < 0) goto ERR; /* send bytes, awaiting echo of each byte */ printf("Downloading code...\n"); /* the 6811 F-series (68-pin PLCC) doesn't need this; it will automatically stop downloading and start running after a timeout. The 6811 A-series (52-pin PLCC; 48-pin DIP) DOES need this: */ if(HiAdr < 255) HiAdr=255; for(Offset=0; Offset < HiAdr; Offset++) { Temp=portWrite(PortAdr, Code[Offset], 1000); if(Temp < 0) goto ERR; Temp=portRead(PortAdr, 1000); if(Temp < 0) goto ERR; /* DJGPP uses buffered output for printf() (i.e. you'd see nothing until the next \n), so use cprintf() */ cprintf("<%02X>", Temp); } /* exit with errorlevel 0 */ printf("\n...done.\n"); exit(0); }