mirror of
git://projects.qi-hardware.com/wernermisc.git
synced 2024-12-01 10:59:42 +02:00
745 lines
14 KiB
C
745 lines
14 KiB
C
|
/*
|
||
|
* picpen.c - PIC (18F{2,4}xJxx) Programmer/Emulator for Nanonote
|
||
|
*
|
||
|
* Written 2012 by Werner Almesberger
|
||
|
* Copyright 2012 Werner Almesberger
|
||
|
*
|
||
|
* 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.
|
||
|
*/
|
||
|
|
||
|
|
||
|
#include <stdint.h>
|
||
|
#include <stdlib.h>
|
||
|
#include <stdio.h>
|
||
|
#include <unistd.h>
|
||
|
#include <string.h>
|
||
|
#include <assert.h>
|
||
|
|
||
|
#include "gpio-xburst.h"
|
||
|
|
||
|
|
||
|
#define POWER_OFF 3, 2 /* PD02 */
|
||
|
#define nMCLR 3, 13 /* PD13 DAT3 */
|
||
|
#define PGD 3, 8 /* PD09 CMD */
|
||
|
#define PGC 3, 9 /* PD08 CLK */
|
||
|
|
||
|
#define ICSP_KEY 0x4d434850
|
||
|
|
||
|
#define P5_US 1 /* Delay Between 4-Bit Command and Command Operand */
|
||
|
#define P5A_US 1 /* Delay Between 4-Bit Command Operand and Next 4-Bit
|
||
|
Command */
|
||
|
#define P6_US 1 /* Delay Between Last PGC down of Command Byte
|
||
|
to First PGC up of Read of Data Word */
|
||
|
#define P9_US 1200 /* Delay to allow Block Programming to Occur */
|
||
|
#define P10_US 54000 /* Delay to allow Row Erase to Occur */
|
||
|
#define P11_US 524000 /* Delay to allow Bulk Erase to Occur */
|
||
|
#define P12_US 400 /* Input Data Hold Time from nMCLR up */
|
||
|
#define P13_US 1 /* VDD up Setup Time to nMCLR up */
|
||
|
#define P16_US 1 /* Delay Between Last PGC down and nMCLR down */
|
||
|
#define P17_US 3 /* nMCLR down to VDD down */
|
||
|
#define P19_US 4000 /* Delay from First nMCLR down to First PGC up for
|
||
|
Key Sequence on PGD */
|
||
|
#define P20_US 1 /* Delay from Last PGC down for Key Sequence on
|
||
|
PGD to Second nMCLR up */
|
||
|
|
||
|
#define CMD_INSN 0
|
||
|
#define CMD_TABLAT 0x2
|
||
|
#define CMD_READ_INC 0x9 /* post-increment by 1 */
|
||
|
#define CMD_WRITE 0xc
|
||
|
#define CMD_WRITE_INC 0xd /* post-increment by 2 */
|
||
|
#define CMD_WRITE_START 0xf
|
||
|
|
||
|
#define INSN_MOVLW 0x0e
|
||
|
#define INSN_MOVWF 0x6e
|
||
|
#define INSN_MOVF_W_0 0x50
|
||
|
#define INSN_MOVFF1 0xcf
|
||
|
#define INSN_MOVFF2 0xff
|
||
|
|
||
|
#define REG_TABLAT 0xf5
|
||
|
#define REG_TBLPTRL 0xf6
|
||
|
#define REG_TBLPTRH 0xf7
|
||
|
#define REG_TBLPTRU 0xf8
|
||
|
|
||
|
#define REG_ANCON0 0x48 /* banked ! */
|
||
|
#define REG_ANCON1 0x49 /* banked ! */
|
||
|
#define REG_PORTA 0x80
|
||
|
#define REG_PORTC 0x82
|
||
|
#define REG_LATA 0x89
|
||
|
#define REG_TRISA 0x92 /* 1 = in */
|
||
|
#define REG_EECON1 0xa6
|
||
|
#define REG_CTMUICON 0xb1
|
||
|
#define REG_CTMUCONL 0xb2
|
||
|
#define REG_CTMUCONH 0xb3
|
||
|
#define REG_ADCON1 0xc1
|
||
|
#define REG_ADCON0 0xc2
|
||
|
#define REG_ADRESL 0xc3
|
||
|
#define REG_ADRESH 0xc4
|
||
|
|
||
|
#define BLOCK 64
|
||
|
|
||
|
|
||
|
struct rec {
|
||
|
uint32_t addr;
|
||
|
uint8_t *data;
|
||
|
int len;
|
||
|
struct rec *next;
|
||
|
};
|
||
|
|
||
|
static int kb;
|
||
|
|
||
|
|
||
|
/* The multiplier (100) is a wild guess. Tested down to 0. */
|
||
|
|
||
|
static inline void delay(int us)
|
||
|
{
|
||
|
uint32_t i;
|
||
|
|
||
|
if (us < 10)
|
||
|
for (i = 0; i != us*100; i++)
|
||
|
asm("");
|
||
|
else
|
||
|
usleep(us);
|
||
|
}
|
||
|
|
||
|
|
||
|
static void icsp_begin(int power)
|
||
|
{
|
||
|
int i;
|
||
|
|
||
|
gpio_init();
|
||
|
|
||
|
gpio_high(nMCLR);
|
||
|
gpio_high(PGD);
|
||
|
gpio_high(PGC);
|
||
|
|
||
|
if (power) {
|
||
|
gpio_output(POWER_OFF);
|
||
|
gpio_output(PGD);
|
||
|
gpio_output(PGC);
|
||
|
gpio_output(nMCLR);
|
||
|
|
||
|
delay(100*1000); /* precharge */
|
||
|
}
|
||
|
|
||
|
gpio_low(PGD);
|
||
|
gpio_low(PGC);
|
||
|
gpio_low(nMCLR);
|
||
|
|
||
|
if (power)
|
||
|
gpio_low(POWER_OFF);
|
||
|
delay(P13_US);
|
||
|
|
||
|
gpio_high(nMCLR);
|
||
|
delay(1);
|
||
|
gpio_low(nMCLR);
|
||
|
delay(P19_US);
|
||
|
|
||
|
for (i = 31; i >= 0; i--) {
|
||
|
if ((ICSP_KEY >> i) & 1)
|
||
|
gpio_high(PGD);
|
||
|
else
|
||
|
gpio_low(PGD);
|
||
|
gpio_high(PGC);
|
||
|
gpio_low(PGC);
|
||
|
}
|
||
|
|
||
|
delay(P20_US);
|
||
|
gpio_high(nMCLR);
|
||
|
delay(P12_US);
|
||
|
}
|
||
|
|
||
|
|
||
|
static void icsp_end(void)
|
||
|
{
|
||
|
gpio_low(PGD);
|
||
|
gpio_low(PGC);
|
||
|
|
||
|
delay(P16_US);
|
||
|
gpio_low(nMCLR);
|
||
|
delay(P17_US);
|
||
|
|
||
|
gpio_high(nMCLR);
|
||
|
// gpio_high(POWER_OFF);
|
||
|
|
||
|
gpio_input(PGD);
|
||
|
gpio_input(PGC);
|
||
|
}
|
||
|
|
||
|
|
||
|
static void icsp_send(uint8_t v, int n)
|
||
|
{
|
||
|
int i;
|
||
|
|
||
|
for (i = 0; i != n; i++) {
|
||
|
if ((v >> i) & 1)
|
||
|
gpio_high(PGD);
|
||
|
else
|
||
|
gpio_low(PGD);
|
||
|
gpio_high(PGC);
|
||
|
gpio_low(PGC);
|
||
|
}
|
||
|
}
|
||
|
|
||
|
|
||
|
static uint8_t icsp_recv(int n)
|
||
|
{
|
||
|
uint8_t v = 0;
|
||
|
int i;
|
||
|
|
||
|
for (i = 0; i != n; i++) {
|
||
|
gpio_high(PGC);
|
||
|
gpio_low(PGC);
|
||
|
if (gpio_get(PGD))
|
||
|
v |= 1 << i;
|
||
|
}
|
||
|
return v;
|
||
|
}
|
||
|
|
||
|
|
||
|
static void icsp_write(uint8_t cmd, uint8_t a, uint8_t b)
|
||
|
{
|
||
|
icsp_send(cmd, 4);
|
||
|
delay(P5_US);
|
||
|
icsp_send(b, 8);
|
||
|
icsp_send(a, 8);
|
||
|
delay(P5A_US);
|
||
|
}
|
||
|
|
||
|
|
||
|
static void write_reg(uint8_t addr, uint8_t v)
|
||
|
{
|
||
|
icsp_write(CMD_INSN, INSN_MOVLW, v);
|
||
|
icsp_write(CMD_INSN, INSN_MOVWF, addr);
|
||
|
}
|
||
|
|
||
|
|
||
|
static uint8_t read_reg(uint8_t addr)
|
||
|
{
|
||
|
uint8_t v;
|
||
|
|
||
|
icsp_write(CMD_INSN, 0x50, addr);
|
||
|
/* for some strange reason we need two writes */
|
||
|
icsp_write(CMD_INSN, INSN_MOVWF, REG_TABLAT);
|
||
|
icsp_write(CMD_INSN, INSN_MOVWF, REG_TABLAT);
|
||
|
|
||
|
icsp_send(CMD_TABLAT, 4);
|
||
|
delay(P5_US);
|
||
|
|
||
|
icsp_send(0, 8);
|
||
|
delay(P6_US);
|
||
|
gpio_input(PGD);
|
||
|
|
||
|
v = icsp_recv(8);
|
||
|
delay(P5A_US);
|
||
|
gpio_output(PGD);
|
||
|
|
||
|
return v;
|
||
|
}
|
||
|
|
||
|
|
||
|
static void set_tblptr(uint32_t addr)
|
||
|
{
|
||
|
write_reg(REG_TBLPTRU, addr >> 16);
|
||
|
write_reg(REG_TBLPTRH, addr >> 8);
|
||
|
write_reg(REG_TBLPTRL, addr);
|
||
|
}
|
||
|
|
||
|
|
||
|
static void read_mem(uint32_t addr, uint8_t *buf, int len)
|
||
|
{
|
||
|
set_tblptr(addr);
|
||
|
|
||
|
while (len--) {
|
||
|
icsp_send(CMD_READ_INC, 4);
|
||
|
delay(P5_US);
|
||
|
icsp_send(0, 8);
|
||
|
delay(P6_US);
|
||
|
gpio_input(PGD);
|
||
|
|
||
|
*buf++ = icsp_recv(8);
|
||
|
delay(P5A_US);
|
||
|
gpio_output(PGD);
|
||
|
}
|
||
|
}
|
||
|
|
||
|
|
||
|
static void bulk_erase(void)
|
||
|
{
|
||
|
set_tblptr(0x3c0005);
|
||
|
icsp_write(CMD_WRITE, 0x01, 0x01);
|
||
|
set_tblptr(0x3c0004);
|
||
|
icsp_write(CMD_WRITE, 0x80, 0x80);
|
||
|
icsp_write(CMD_INSN, 0, 0);
|
||
|
|
||
|
icsp_send(0, 4);
|
||
|
delay(P11_US+P10_US);
|
||
|
|
||
|
icsp_send(0, 16);
|
||
|
delay(P5A_US); /* guess*/
|
||
|
}
|
||
|
|
||
|
|
||
|
static void write_mem(uint32_t addr, const uint8_t *buf, int len)
|
||
|
{
|
||
|
int i;
|
||
|
uint8_t first;
|
||
|
|
||
|
assert(!(addr & (BLOCK-1)));
|
||
|
assert(!(len & (BLOCK-1)));
|
||
|
|
||
|
/* BSF EECON1, WREN */
|
||
|
icsp_write(CMD_INSN, 0x84, REG_EECON1);
|
||
|
|
||
|
while (len) {
|
||
|
set_tblptr(addr);
|
||
|
for (i = 0; i != BLOCK/2-1; i++) {
|
||
|
first = *buf++;
|
||
|
icsp_write(CMD_WRITE_INC, *buf++, first);
|
||
|
}
|
||
|
first = *buf++;
|
||
|
icsp_write(CMD_WRITE_START, *buf++, first);
|
||
|
|
||
|
icsp_send(0, 3);
|
||
|
gpio_low(PGD);
|
||
|
gpio_high(PGC);
|
||
|
delay(P9_US);
|
||
|
gpio_low(PGC);
|
||
|
delay(P5_US);
|
||
|
|
||
|
icsp_send(0, 16);
|
||
|
delay(P5A_US); /* guess*/
|
||
|
|
||
|
addr += BLOCK;
|
||
|
len -= BLOCK;
|
||
|
}
|
||
|
}
|
||
|
|
||
|
|
||
|
/* ----- Flash-level operations -------------------------------------------- */
|
||
|
|
||
|
|
||
|
static void flash_record(const struct rec *rec)
|
||
|
{
|
||
|
uint8_t *tmp;
|
||
|
int off, padded;
|
||
|
|
||
|
off = rec->addr & (BLOCK-1);
|
||
|
padded = (rec->len+off+BLOCK-1) & ~(BLOCK-1);
|
||
|
if (!off && padded == rec->len) {
|
||
|
write_mem(rec->addr, rec->data, rec->len);
|
||
|
return;
|
||
|
}
|
||
|
tmp = malloc(padded);
|
||
|
if (!tmp) {
|
||
|
perror("malloc");
|
||
|
exit(1);
|
||
|
}
|
||
|
memset(tmp, 0xff, off);
|
||
|
memcpy(tmp+off, rec->data, rec->len);
|
||
|
memset(tmp+off+rec->len, 0xff, padded-rec->len-off);
|
||
|
write_mem(rec->addr-off, tmp, padded);
|
||
|
}
|
||
|
|
||
|
|
||
|
static void verify_record(const struct rec *rec)
|
||
|
{
|
||
|
uint8_t *tmp;
|
||
|
int i;
|
||
|
|
||
|
tmp = malloc(rec->len);
|
||
|
if (!tmp) {
|
||
|
perror("malloc");
|
||
|
exit(1);
|
||
|
}
|
||
|
read_mem(rec->addr, tmp, rec->len);
|
||
|
for (i = 0; i != rec->len; i++)
|
||
|
if (rec->data[i] != tmp[i]) {
|
||
|
fprintf(stderr,
|
||
|
"%04x: wrote %02x != read %02x\n",
|
||
|
rec->addr+i, rec->data[i], tmp[i]);
|
||
|
}
|
||
|
}
|
||
|
|
||
|
|
||
|
static void flash_file(const struct rec *recs)
|
||
|
{
|
||
|
const struct rec *rec;
|
||
|
|
||
|
for (rec = recs; rec; rec = rec->next)
|
||
|
if (rec->addr+rec->len > kb*1024) {
|
||
|
fprintf(stderr,
|
||
|
"record 0x%x+0x%x ends outside Flash of %d bytes\n",
|
||
|
rec->addr, rec->len, kb*1024-8);
|
||
|
exit(1);
|
||
|
}
|
||
|
|
||
|
bulk_erase();
|
||
|
for (rec = recs; rec; rec = rec->next)
|
||
|
flash_record(rec);
|
||
|
|
||
|
for (rec = recs; rec; rec = rec->next)
|
||
|
verify_record(rec);
|
||
|
}
|
||
|
|
||
|
|
||
|
static void dump_flash(void)
|
||
|
{
|
||
|
uint8_t *tmp, c;
|
||
|
int i, j;
|
||
|
|
||
|
tmp = malloc(kb*1024);
|
||
|
if (!tmp) {
|
||
|
perror("malloc");
|
||
|
exit(1);
|
||
|
}
|
||
|
|
||
|
read_mem(0, tmp, kb*1024);
|
||
|
|
||
|
for (i = 0; i != kb*1024; i += 16) {
|
||
|
printf("%04X: ", i);
|
||
|
for (j = 0; j != 16; j++)
|
||
|
printf("%02X ", tmp[i+j]);
|
||
|
for (j = 0; j != 16; j++) {
|
||
|
c = tmp[i+j];
|
||
|
printf("%c", c >= ' ' && c <= '~' ? c : '.');
|
||
|
}
|
||
|
printf("\n");
|
||
|
}
|
||
|
}
|
||
|
|
||
|
|
||
|
/* ----- Experiments ------------------------------------------------------- */
|
||
|
|
||
|
|
||
|
#if 0
|
||
|
|
||
|
static void blink(void)
|
||
|
{
|
||
|
write_reg(REG_TRISA, 0xfc);
|
||
|
while (1) {
|
||
|
write_reg(REG_LATA, 1);
|
||
|
gpio_high(PGD);
|
||
|
delay(200*1000);
|
||
|
write_reg(REG_LATA, 2);
|
||
|
gpio_high(PGD);
|
||
|
delay(200*1000);
|
||
|
}
|
||
|
}
|
||
|
|
||
|
|
||
|
static void adc(void)
|
||
|
{
|
||
|
int i;
|
||
|
|
||
|
#if 0
|
||
|
for (i = 0; i != 256; i++) {
|
||
|
// write_reg(REG_ANCON0, 0xff);
|
||
|
// write_reg(REG_TABLAT, i);
|
||
|
// write_reg(REG_TABLAT, i);
|
||
|
printf("%02x ", read_reg(REG_PORTC));
|
||
|
fflush(stdout);
|
||
|
}
|
||
|
#endif
|
||
|
write_reg(REG_TRISA, 0xfc);
|
||
|
// write_reg(REG_ANCON0, 0x10); /* AN4 is analog */
|
||
|
write_reg(REG_ADCON0, 0x20); /* AN4, Vss to AVdd */
|
||
|
write_reg(REG_ADCON1, 0x80); /* Tad = 0, Fosc/2, right just. */
|
||
|
write_reg(REG_ADCON0, 0x21); /* Enable ADC module */
|
||
|
while (1) {
|
||
|
uint8_t hi, lo;
|
||
|
|
||
|
write_reg(REG_ADCON0, 0x23); /* GO */
|
||
|
while (read_reg(REG_ADCON0) & 2)
|
||
|
write(2, ".", 1);
|
||
|
hi = read_reg(REG_ADRESH);
|
||
|
lo = read_reg(REG_ADRESL);
|
||
|
printf("\r%02x %02x (%u)\n", hi, lo, hi << 8 | lo);
|
||
|
}
|
||
|
|
||
|
#if 0
|
||
|
while (1) {
|
||
|
write_reg(
|
||
|
}
|
||
|
#endif
|
||
|
|
||
|
}
|
||
|
|
||
|
|
||
|
static void cap(void)
|
||
|
{
|
||
|
write_reg(REG_CTMUCONH, 0x00); /* page 405 */
|
||
|
write_reg(REG_CTMUCONL, 0x90);
|
||
|
write_reg(REG_CTMUICON, 0x01);
|
||
|
|
||
|
write_reg(REG_TRISA, 0xfc);
|
||
|
write_reg(REG_ADCON0, 0x20); /* AN4, Vss to AVdd */
|
||
|
// write_reg(REG_ADCON1, 0x8e); /* Tad = 2, Fosc/32, right just. */
|
||
|
write_reg(REG_ADCON1, 0x88); /* Tad = 2, Fosc/2, right just. */
|
||
|
write_reg(REG_ADCON0, 0x21); /* Enable ADC module */
|
||
|
|
||
|
/* bandgap ?!? */
|
||
|
|
||
|
while (1) {
|
||
|
uint8_t hi, lo;
|
||
|
|
||
|
write_reg(REG_CTMUCONH, 0x80); /* enable CTMU */
|
||
|
write_reg(REG_CTMUCONL, 0x90); /* clear EDGxSTAT */
|
||
|
write_reg(REG_CTMUCONH, 0x82); /* ground output */
|
||
|
delay(1);
|
||
|
write_reg(REG_CTMUCONH, 0x80); /* end drain */
|
||
|
write_reg(REG_CTMUCONL, 0x91); /* current on */
|
||
|
write_reg(REG_CTMUCONL, 0x90); /* current off */
|
||
|
|
||
|
write_reg(REG_ADCON0, 0x23); /* GO */
|
||
|
while (read_reg(REG_ADCON0) & 2)
|
||
|
write(2, ".", 1);
|
||
|
hi = read_reg(REG_ADRESH);
|
||
|
lo = read_reg(REG_ADRESL);
|
||
|
printf("\r%02x %02x (%u)\n", hi, lo, hi << 8 | lo);
|
||
|
}
|
||
|
}
|
||
|
|
||
|
|
||
|
static void dump(uint32_t addr)
|
||
|
{
|
||
|
uint8_t buf[BLOCK];
|
||
|
int i;
|
||
|
|
||
|
read_mem(addr, buf, BLOCK);
|
||
|
for (i = 0; i != BLOCK; i++) {
|
||
|
if (!(i & 15))
|
||
|
printf("%04X:", i);
|
||
|
printf(" %02X", buf[i]);
|
||
|
if ((i & 15) == 15)
|
||
|
printf("\n");
|
||
|
}
|
||
|
}
|
||
|
|
||
|
|
||
|
static void rerwr(void)
|
||
|
{
|
||
|
uint8_t buf[BLOCK];
|
||
|
int i;
|
||
|
|
||
|
dump(0x3fffc0);
|
||
|
dump(0);
|
||
|
bulk_erase();
|
||
|
dump(0);
|
||
|
for (i = 0; i != BLOCK; i++)
|
||
|
buf[i] = i;
|
||
|
write_mem(0, buf, BLOCK);
|
||
|
dump(0x3fffc0);
|
||
|
dump(0);
|
||
|
}
|
||
|
|
||
|
#endif
|
||
|
|
||
|
/* ----- Chip identification ----------------------------------------------0 */
|
||
|
|
||
|
|
||
|
struct chip {
|
||
|
const char *name;
|
||
|
uint8_t id2, id1;
|
||
|
int kb;
|
||
|
} chips[] = {
|
||
|
{ "PIC18F24J50", 0x4c, 0x00, 16 },
|
||
|
{ NULL }
|
||
|
};
|
||
|
|
||
|
|
||
|
#define DEVID1 0x3ffffe
|
||
|
|
||
|
|
||
|
static void identify(void)
|
||
|
{
|
||
|
uint8_t id[2];
|
||
|
const struct chip *chip;
|
||
|
|
||
|
read_mem(DEVID1, id, 2);
|
||
|
for (chip = chips; chip->name; chip++) {
|
||
|
if ((chip->id1 ^ id[0]) & 0xe0)
|
||
|
continue;
|
||
|
if (chip->id2 == id[1])
|
||
|
break;
|
||
|
}
|
||
|
fprintf(stderr, "ID = 0x%02x%02x (%s)\n", id[1], id[0],
|
||
|
chip->name ? chip->name : "?");
|
||
|
if (!chip->name)
|
||
|
exit(1);
|
||
|
fprintf(stderr, "%d kB Flash\n", chip->kb);
|
||
|
kb = chip->kb;
|
||
|
}
|
||
|
|
||
|
|
||
|
/* ----- Intel HEX file reader --------------------------------------------- */
|
||
|
|
||
|
|
||
|
static int hex(char c, int lineno)
|
||
|
{
|
||
|
if (c >= '0' && c <= '9')
|
||
|
return c-'0';
|
||
|
if (c >= 'A' && c <= 'F')
|
||
|
return c-'A'+10;
|
||
|
fprintf(stderr, "non-hex character \"%c\" in line %d\n", c, lineno);
|
||
|
exit(1);
|
||
|
}
|
||
|
|
||
|
|
||
|
/* http://en.wikipedia.org/wiki/Intel_HEX */
|
||
|
|
||
|
|
||
|
static struct rec *load_file(const char *name)
|
||
|
{
|
||
|
struct rec *recs = NULL, *last = NULL, *rec;
|
||
|
const struct rec *other;
|
||
|
FILE *file;
|
||
|
int lineno = 1;
|
||
|
char line[1000];
|
||
|
const char *s;
|
||
|
uint8_t buf[sizeof(line)/2];
|
||
|
uint8_t *p, *t, sum;
|
||
|
uint32_t xaddr = 0, addr;
|
||
|
|
||
|
file = fopen(name, "r");
|
||
|
if (!file) {
|
||
|
perror(name);
|
||
|
exit(1);
|
||
|
}
|
||
|
while (fgets(line, sizeof(line), file)) {
|
||
|
if (*line != ':') {
|
||
|
fprintf(stderr, "line %d doesn't start with colon\n",
|
||
|
lineno);
|
||
|
exit(1);
|
||
|
}
|
||
|
p = buf;
|
||
|
for (s = line+1; *s > ' '; s += 2)
|
||
|
*p++ = hex(s[0], lineno) << 4 | hex(s[1], lineno);
|
||
|
if (p-buf < 5) {
|
||
|
fprintf(stderr, "short record in line %d\n", lineno);
|
||
|
exit(1);
|
||
|
}
|
||
|
lineno++;
|
||
|
sum = 0;
|
||
|
for (t = buf; t != p-1; t++)
|
||
|
sum += *t;
|
||
|
if (0x100-sum != p[-1]) {
|
||
|
fprintf(stderr,
|
||
|
"checksum error (0x%02x vs. 0x%02x) in line %d\n",
|
||
|
p[-1], 0x100-sum, lineno);
|
||
|
exit(1);
|
||
|
}
|
||
|
switch (buf[3]) {
|
||
|
case 0: /* Data record */
|
||
|
if (p-buf != buf[0]+5) {
|
||
|
fprintf(stderr,
|
||
|
"data record of %d bytes has length %d\n",
|
||
|
p-buf, buf[0]);
|
||
|
exit(1);
|
||
|
}
|
||
|
addr = xaddr << 16 | buf[1] << 8 | buf[2];
|
||
|
if (last && last->addr+last->len == addr) {
|
||
|
last->data = realloc(last->data,
|
||
|
last->len+buf[0]);
|
||
|
if (!last->data) {
|
||
|
perror("realloc");
|
||
|
exit(1);
|
||
|
}
|
||
|
memcpy(last->data+last->len, buf+4, buf[0]);
|
||
|
last->len += buf[0];
|
||
|
break;
|
||
|
}
|
||
|
rec = malloc(sizeof(struct rec));
|
||
|
rec->addr = xaddr << 16 | buf[1] << 8 | buf[2];
|
||
|
rec->len = buf[0];
|
||
|
rec->data = malloc(rec->len);
|
||
|
if (!rec->data) {
|
||
|
perror("realloc");
|
||
|
exit(1);
|
||
|
}
|
||
|
rec->next = NULL;
|
||
|
memcpy(rec->data, buf+4, rec->len);
|
||
|
if (last)
|
||
|
last->next = rec;
|
||
|
else
|
||
|
recs = rec;
|
||
|
last = rec;
|
||
|
break;
|
||
|
case 1: /* End Of File record */
|
||
|
goto end;
|
||
|
case 4: /* Extended Linear Address record */
|
||
|
xaddr = buf[4] << 8 | buf[5];
|
||
|
break;
|
||
|
default:
|
||
|
fprintf(stderr,
|
||
|
"unrecognized record type 0x%02x in line %d\n",
|
||
|
buf[3], lineno);
|
||
|
exit(1);
|
||
|
}
|
||
|
}
|
||
|
end:
|
||
|
for (rec = recs; rec; rec = rec->next)
|
||
|
for (other = rec->next; other; other = other->next)
|
||
|
if (rec->addr < other->addr+other->len &&
|
||
|
rec->addr+rec->len > other->addr) {
|
||
|
fprintf(stderr,
|
||
|
"overlapping address ranges 0x%x+0x%x "
|
||
|
"and 0x%x+0x%x\n",
|
||
|
rec->addr, rec->len, other->addr,
|
||
|
other->len);
|
||
|
}
|
||
|
fclose(file);
|
||
|
return recs;
|
||
|
}
|
||
|
|
||
|
|
||
|
/* ----- Command-line parsing ---------------------------------------------- */
|
||
|
|
||
|
|
||
|
static void usage(const char *name)
|
||
|
{
|
||
|
fprintf(stderr,
|
||
|
"usage: %s [-n] [file.bin]\n", name);
|
||
|
exit(1);
|
||
|
}
|
||
|
|
||
|
|
||
|
int main(int argc, char **argv)
|
||
|
{
|
||
|
const struct rec *recs = NULL;
|
||
|
int power = 1;
|
||
|
int c;
|
||
|
|
||
|
while ((c = getopt(argc, argv, "n")) != EOF)
|
||
|
switch (c) {
|
||
|
case 'n':
|
||
|
power = 0;
|
||
|
break;
|
||
|
default:
|
||
|
usage(*argv);
|
||
|
}
|
||
|
|
||
|
switch (argc-optind) {
|
||
|
case 0:
|
||
|
break;
|
||
|
case 1:
|
||
|
recs = load_file(argv[optind]);
|
||
|
break;
|
||
|
default:
|
||
|
usage(*argv);
|
||
|
}
|
||
|
|
||
|
icsp_begin(power);
|
||
|
identify();
|
||
|
|
||
|
if (recs)
|
||
|
flash_file(recs);
|
||
|
else
|
||
|
dump_flash();
|
||
|
icsp_end();
|
||
|
|
||
|
return 0;
|
||
|
}
|