diff options
Diffstat (limited to 'avr/cmd_loadihex.c')
-rw-r--r-- | avr/cmd_loadihex.c | 216 |
1 files changed, 216 insertions, 0 deletions
diff --git a/avr/cmd_loadihex.c b/avr/cmd_loadihex.c new file mode 100644 index 0000000..19cd29b --- /dev/null +++ b/avr/cmd_loadihex.c @@ -0,0 +1,216 @@ +/* + * (C) Copyright 2015 Leo C. <erbl259-lmu@yahoo.de> + * + * SPDX-License-Identifier: GPL-2.0 + */ + +#include "cmd_loadihex.h" + +#include "con-utils.h" +#include "z80-if.h" +#include "debug.h" + + +typedef enum { + IHX_OK, + IHX_BROKEN, + IHX_CHKSUMERR +} ihx_rstat_t; + +typedef struct { + ihx_rstat_t status; + int8_t type; + uint8_t len; + uint16_t address; + uint8_t data[256]; +} ihex_t; + + +static int get_hexdigit(void) +{ + int c; + c = toupper(my_getchar(1)); + if (isxdigit(c)) { + c -= '0'; + if (c > 9) + c -= ('A' - '0' - 10); + return c; + } else + return -1; +} + +static int get_hexbyte(void) +{ + uint8_t i,j; + + if ((i = (uint8_t) get_hexdigit()) < 0x10) + if ((j = (uint8_t) get_hexdigit()) < 0x10) { + return (i<<4) + j; + } + + return -1; +} + + +static int ihex_get_record(ihex_t *rec) +{ + int c; + uint8_t sum; + + rec->status = IHX_BROKEN; + rec->len = 0; + rec->type = 0xff; + + while ((c = my_getchar(0)) != ':') { + if (c == 0x03) + return -1; /* Control-C */ + if (c == 0x04) { + rec->status = IHX_OK; + return 0; /*Control-D, EOF */ + } + } + + if ((c = get_hexbyte()) < 0) /* Start code */ + return -1; + sum = c; + rec->len = c; + if ((c = get_hexbyte()) < 0) /* Byte Count */ + return -1; + sum += c; + rec->address = c * 256; + if ((c = get_hexbyte()) < 0) /* Address */ + return -1; + sum += c; + rec->address += c; + if ((c = get_hexbyte()) < 0) /* Record type */ + return -1; + sum += c; + rec->type = c; + + if (rec->len) { /* Record Data */ + uint8_t n; + + for (n = 0; n < rec->len; n++) { + if ((c = get_hexbyte()) < 0) + break; + sum += c; + rec->data[n] = c; + } + + if (n < rec->len) { + return -1; + } + } + + c = get_hexbyte(); /* Check sum */ + + if (c >= 0) { + sum += c; + if (sum == 0) + rec->status = IHX_OK; + else + rec->status = IHX_CHKSUMERR; + } + + return rec->len; +} + + +command_ret_t do_loadihex(cmd_tbl_t *cmdtp UNUSED, uint_fast8_t flag UNUSED, int argc, char * const argv[]) +{ + long offset = 0; + uint32_t base_address = 0; + uint32_t address_high = 0; + uint32_t address_max; + uint32_t address_low; + bool firstrec = true; + ihex_t rec; + + if (argc > 1) + offset = strtol(argv[1], NULL, 16); + + int32_t ram = z80_memsize_detect(); + if (ram < 0) + cmd_error(CMD_RET_FAILURE, (ERRNUM) -ram, NULL); + + address_max = ram; + address_low = address_max; + + my_puts_P(PSTR("Waiting for Intel Hex Records...\n")); + + while (ihex_get_record(&rec) > 0 && + rec.status == IHX_OK && + rec.type != 1 ) { + + switch (rec.type) { + case 0: /* Data record */ + if (firstrec) { + my_puts_P(PSTR("Loading: 0x.....")); + firstrec = false; + } + if (rec.len) { + uint32_t addr = base_address + rec.address + offset; + if (addr < address_low) + address_low = addr; + if (addr+rec.len > address_high) + address_high = addr + rec.len; + +// debug("low: 0x%.5lX, high: 0x%.5lX, max: 0x%.5lX, addr: 0x%.5lX, len: %d\n", +// address_low, address_high, address_max, addr, rec.len); + printf_P(PSTR("\b\b\b\b\b%.5lX"), addr); + if (addr < address_max) { + uint32_t tmplen = address_max - addr; + if (rec.len > tmplen) + rec.len = tmplen; + + z80_bus_request_or_exit(); + z80_write_block(rec.data, /* src */ + addr, /* dest */ + rec.len); /* len */ + z80_bus_cmd(Release); + } + } + break; + +#if 0 + case 1: /* EOF record */ + break; +#endif + case 2: /* Extended Segment Address Record */ + base_address = (uint32_t)((rec.data[0] << 8) + rec.data[1]) << 4; + break; + + case 4: /* Extended Linear Address Record */ + base_address = (uint32_t)((rec.data[0] << 8) + rec.data[1]) << 16; + break; + + case 3: /* Start Segment Address Record (ignored)*/ + case 5: /* Start Linear Address Record (ignored)*/ + break; + + } + } + + if (rec.status != IHX_OK) + my_puts_P(PSTR("Broken Hex Record or loading interrupted!\n")); + + for (uint_fast8_t i=0; i<100; ++i) { + /* flush input buffer */ + while (my_getchar(0) > 0) + ; + udelay(1000); + } + + my_puts_P(PSTR("\nData loaded: ")); + if (address_low >= MIN(address_high, address_max)) + my_puts_P(PSTR("None.\n")); + else + printf_P(PSTR("low: 0x%.5lX high: 0x%.5lX\n"), address_low, + MIN(address_high, address_max) - 1); + + if (address_high > address_max) + printf_P(PSTR("Data above highest RAM address " + "(in range 0x%.5lX - 0x%.5lX) ignored!\n"), address_max, address_high - 1); + + return rec.status == IHX_OK ? CMD_RET_SUCCESS : CMD_RET_FAILURE; +} |