// // srecord - manipulate eprom load files // Copyright (C) 1998-2003, 2006, 2007 Peter Miller // // 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 3 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 General Public License for more details. // // You should have received a copy of the GNU General Public License // along with this program. If not, see // . // #include #include #include #include #include bool srec_memory::overwrite = false; srec_memory::srec_memory() : nchunks(0), nchunks_max(0), chunk(0), cache(0), find_next_chunk_index(0), header(0), start_address(0) { } srec_memory::srec_memory(const srec_memory &arg) : nchunks(0), nchunks_max(0), chunk(0), cache(0), find_next_chunk_index(0), header(0), start_address(0) { copy(arg); } srec_memory & srec_memory::operator=(const srec_memory &arg) { if (&arg != this) { clear(); copy(arg); } return *this; } srec_memory::~srec_memory() { clear(); } void srec_memory::clear() { delete header; header = 0; delete start_address; start_address = 0; for (int j = 0; j < nchunks; ++j) delete chunk[j]; if (chunk) delete [] chunk; nchunks = 0; nchunks_max = 0; chunk = 0; cache = 0; } void srec_memory::copy(const srec_memory &arg) { delete header; header = 0; if (arg.header) header = new srec_record(*arg.header); delete start_address; start_address = 0; if (arg.start_address) start_address = new srec_record(*arg.start_address); nchunks = arg.nchunks; while (nchunks_max < nchunks) nchunks_max = nchunks_max * 2 + 4; chunk = new srec_memory_chunk * [nchunks_max]; for (int j = 0; j < nchunks; ++j) { // use copy-new to make the copies chunk[j] = new srec_memory_chunk(*(arg.chunk[j])); } } srec_memory_chunk * srec_memory::find(unsigned long address) const { // // Speed things up if we've been there recently. // if (cache && cache->get_address() == address) return cache; // // Binary chop to find the appropriate chunk. // int min = 0; int max = nchunks - 1; srec_memory_chunk *mcp = 0; while (min <= max) { int mid = (min + max) / 2; mcp = chunk[mid]; if (mcp->get_address() == address) { cache = mcp; return cache; } if (address < mcp->get_address()) max = mid - 1; else min = mid + 1; } // // We need a new row. Make sure there is enough room. // if (nchunks >= nchunks_max) { nchunks_max = nchunks_max * 2 + 4; srec_memory_chunk **tmp = new srec_memory_chunk * [nchunks_max]; for (int j = 0; j < nchunks; ++j) tmp[j] = chunk[j]; delete [] chunk; chunk = tmp; } // // Insert the new chunk. // mcp = new srec_memory_chunk(address); for (int up = nchunks; up > min; --up) chunk[up] = chunk[up - 1]; ++nchunks; chunk[min] = mcp; cache = mcp; return mcp; } void srec_memory::set(unsigned long address, int datum) { unsigned long address_hi = address / srec_memory_chunk::size; unsigned long address_lo = address % srec_memory_chunk::size; srec_memory_chunk *mcp = find(address_hi); mcp->set(address_lo, datum); } int srec_memory::get(unsigned long address) const { unsigned long address_hi = address / srec_memory_chunk::size; unsigned long address_lo = address % srec_memory_chunk::size; srec_memory_chunk *mcp = find(address_hi); return mcp->get(address_lo); } bool srec_memory::set_p(unsigned long address) const { unsigned long address_hi = address / srec_memory_chunk::size; unsigned long address_lo = address % srec_memory_chunk::size; srec_memory_chunk *mcp = find(address_hi); return mcp->set_p(address_lo); } bool srec_memory::equal(const srec_memory &lhs, const srec_memory &rhs) { if (lhs.nchunks != rhs.nchunks) return false; for (int j = 0; j < lhs.nchunks; ++j) if (lhs.chunk[j][0] != rhs.chunk[j][0]) return false; return true; } bool srec_memory::compare(const srec_memory &lhs, const srec_memory &rhs) { srec_memory_walker_compare wlhs(rhs, true); lhs.walk(&wlhs); wlhs.print("Left"); srec_memory_walker_compare wrhs(lhs, false); rhs.walk(&wrhs); wrhs.print("Right"); return (!wlhs.same() || !wrhs.same()); } unsigned long srec_memory::get_upper_bound() const { if (nchunks == 0) return 0; return chunk[nchunks - 1]->get_upper_bound(); } void srec_memory::walk(srec_memory_walker *w) const { w->notify_upper_bound(get_upper_bound()); w->observe_header(get_header()); for (int j = 0; j < nchunks; ++j) chunk[j]->walk(w); // Only write a start address record if we were given one. if (start_address) w->observe_start_address(get_start_address()); } void srec_memory::reader(srec_input *ifp, bool barf) { srec_record record; while (ifp->read(record)) { switch (record.get_type()) { case srec_record::type_header: if (!header) { header = new srec_record(record); } break; case srec_record::type_unknown: case srec_record::type_data_count: break; case srec_record::type_data: // // For each data byte, we have to check for duplicates. We // issue warnings for redundant settings, and we issue error // for contradictory settings. // for (int j = 0; j < record.get_length(); ++j) { srec_record::address_t address = record.get_address() + j; int n = record.get_data(j); if (barf && set_p(address)) { int old = get(address); if (n == old) { ifp->warning("redundant %08lX value", (long)address); } else if (overwrite) { ifp->warning ( "multiple %08lX values (previous = %02X, " "this one = %02X)", (long)address, old, n ); } else { ifp->fatal_error ( "contradictory %08lX value (previous = %02X, " "this one = %02X)", (long)address, old, n ); } } set(address, n); } break; case srec_record::type_start_address: if (!start_address) { start_address = new srec_record(record); } break; } } } bool operator == (const srec_memory &lhs, const srec_memory &rhs) { return srec_memory::equal(lhs, rhs); } bool operator != (const srec_memory &lhs, const srec_memory &rhs) { return !srec_memory::equal(lhs, rhs); } srec_memory_chunk * srec_memory::find_next_chunk(unsigned long address) const { // // This method is generally called sequentially, to visit each // and every byte, in cases where walk() is not appropriate. // As such, a binary chop isn't necessary. // if (find_next_chunk_index < nchunks) { srec_memory_chunk *mcp = chunk[find_next_chunk_index]; if (mcp->get_address() > address) find_next_chunk_index = 0; } while (find_next_chunk_index < nchunks) { srec_memory_chunk *mcp = chunk[find_next_chunk_index]; if (mcp->get_address() >= address) return mcp; find_next_chunk_index++; } return 0; } bool srec_memory::find_next_data(unsigned long &address, void *data, size_t &nbytes) const { unsigned long address_hi = address / srec_memory_chunk::size; for (;;) { srec_memory_chunk *mcp = find_next_chunk(address_hi); if (!mcp) return false; if (mcp->find_next_data(address, data, nbytes)) return true; address_hi = mcp->get_address() + 1; address = address_hi * srec_memory_chunk::size; } } void srec_memory::allow_overwriting() { overwrite = true; } srec_record * srec_memory::get_header() const { return header; } void srec_memory::set_header(const char *s) { delete header; size_t len = strlen(s); if (len > srec_record::max_data_length) len = srec_record::max_data_length; header = new srec_record ( srec_record::type_header, 0, (srec_record::data_t *)s, len ); } srec_record * srec_memory::get_start_address() const { return start_address; } void srec_memory::set_start_address(unsigned long addr) { delete start_address; start_address = new srec_record(srec_record::type_start_address, addr, 0, 0); } bool srec_memory::has_holes() const { srec_memory_walker_continuity sniffer; walk(&sniffer); return (!sniffer.is_continuous()); }