/* * (C) Copyright 2015 Leo C. * * SPDX-License-Identifier: GPL-2.0+ */ #include "common.h" #include #include #include #include "command.h" #include "con-utils.h" #include "z80-if.h" #include "debug.h" uint32_t detect_ramsize(void) { uint32_t addr; uint8_t save_addr, save_0; const uint8_t PATTERN_1 = 0x55; const uint8_t PATTERN_2 = ~PATTERN_1; if (!(z80_bus_cmd(Request) & ZST_ACQUIRED)) { my_puts_P(PSTR("Bus timeout\n")); return 0; } save_0 = z80_read(0); z80_write(0, PATTERN_1); for (addr=1; addr < CONFIG_SYS_RAMSIZE_MAX; addr <<= 1) { save_addr = z80_read(addr); z80_write(addr, PATTERN_2); if (z80_read(0) != PATTERN_1 || z80_read(addr) != PATTERN_2) break; z80_write(addr, save_addr); } z80_write(0, save_0); z80_bus_cmd(Release); return addr; } 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, int flag, int argc, char * const argv[]) { long offset = 0; uint32_t base_address = 0; uint32_t address_max = detect_ramsize(); uint32_t address_high = 0; uint32_t address_low = address_max; command_ret_t rcode = CMD_RET_FAILURE; ihex_t rec; bool firstrec = true; (void) cmdtp; (void) flag; if (argc > 1) offset = strtol(argv[1], NULL, 16); printf_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) { printf_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; if (!(z80_bus_cmd(Request) & ZST_ACQUIRED)) { my_puts_P(PSTR("Bus timeout\n")); return CMD_RET_FAILURE; } 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; } } switch (rec.status) { case IHX_OK: rcode = CMD_RET_SUCCESS; break; case IHX_BROKEN: case IHX_CHKSUMERR: printf_P(PSTR("Broken Hex Record or loading interrupted!\n")); break; } for (uint_fast8_t i=0; i<100; ++i) { /* flush input buffer */ while (my_getchar(0) > 0) ; udelay(1000); } printf_P(PSTR("\nData loaded: ")); if (address_low >= MIN(address_high, address_max)) printf_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 rcode; }