001package jmri.jmrix.can.cbus.swing.bootloader; 002 003import java.io.IOException; 004import java.util.Arrays; 005 006import org.slf4j.Logger; 007import org.slf4j.LoggerFactory; 008 009/** 010 * Class to encapsulate a hex record as used by Microchip tools. 011 * 012 * @author Andrew Crosland Copyright (C) 2020 013 */ 014public class HexRecord { 015 016 // Record types 017 static final byte EXT_ADDR = 4; 018 static final byte TYPE_DATA = 0; 019 static final byte END = 1; 020 // Maximum data length 021 static final int MAX_LEN = 256; 022 // Offsets of fields within the record 023 024 // Record property members 025 protected int len; 026 protected int addrh; 027 protected int addrl; 028 protected int address; 029 protected int type; 030 protected int checksum; 031 protected boolean valid; 032 protected byte [] data; 033 protected int lineNo; 034 035 036 /** 037 * Create an empty record with unprogrammed data and invalid status. 038 */ 039 public HexRecord() { 040 len = 0; 041 addrh = 0; 042 addrl = 0; 043 type = END; 044 checksum = 0; 045 valid = false; 046 data = new byte[MAX_LEN]; 047 Arrays.fill(data, (byte)0xFF); 048 lineNo = -1; 049 } 050 051 052 /** 053 * Read a new record from a file. 054 * 055 * @param f hex file to read from 056 * @throws IOException from underlying read operations 057 */ 058 public HexRecord(HexFile f) throws IOException { 059 this(); 060 readRecord(f); 061 } 062 063 064 /** 065 * Set the line number where the record was found in the file. 066 * 067 * @param l the line number 068 */ 069 protected void setLineNo(int l) { 070 lineNo = l; 071 } 072 073 074 /** 075 * Get the data array from a hex record. 076 * 077 * @return the data 078 */ 079 protected byte [] getData() { 080 return data; 081 } 082 083 084 /** 085 * Get a data element from a hex record. 086 * 087 * @param i index of the element to get 088 * @return the data 089 */ 090 protected byte getData(int i) { 091 return data[i]; 092 } 093 094 095 /** 096 * Get current address from a hex record. 097 * <p> 098 * Returns 16 bit address from a normal hex record. Extended address records 099 * are handled elsewhere. 100 * 101 * @return the address 102 */ 103 protected int getAddress() { 104 return address; 105 } 106 107 108 /** 109 * Look for the start of a new record. 110 * 111 * @param f Input hex file 112 */ 113 private void startRecord(HexFile f) throws IOException { 114 int c; 115 116 checksum = 0; 117 // Skip newline and look for ':' 118 while (((c = f.readChar()) == 0xd) || (c == 0xa)) { } 119 if (c != ':') { 120 log.error("No colon at start of line {}", f.getLineNo()); 121 throw new IOException("No colon at start of line "+f.getLineNo()); 122 } 123 } 124 125 126 /** 127 * Read hex record header. 128 * 129 * @param f Input hex file 130 * @throws IOException 131 */ 132 private void readHeader(HexFile f) throws IOException { 133 // length of data 134 len = f.rdHexByte(); 135 checksum += len; 136 // High address 137 addrh = f.rdHexByte(); 138 checksum += addrh; 139 // Low address 140 addrl = f.rdHexByte(); 141 checksum += addrl; 142 // record type 143 type = f.rdHexByte(); 144 checksum += type; 145 if (type != EXT_ADDR) { 146 // update address, extended address should be handled by caller 147 address = addrh * 256 + addrl; 148 } 149 } 150 151 152 /** 153 * Read the data bytes. 154 * 155 * @param f Input hex file 156 * @throws IOException from underlying read operations 157 */ 158 void readData(HexFile f) throws IOException { 159 if (type != END) { 160 for (int i = 0; i < len; i++) { 161 data[i] = (byte)(f.rdHexByte() & 0xFF); 162 checksum += data[i]; 163 } 164 } 165 } 166 167 168 /** 169 * Verify the record checksum. 170 * 171 * @param f Input hex file 172 * @throws IOException 173 */ 174 private void checkRecord(HexFile f) throws IOException { 175 int fileCheck = f.rdHexByte(); 176 if (((checksum + fileCheck) & 0xff) != 0) { 177 log.error("Bad checksum at {}", Integer.toHexString(address)); 178 throw new IOException("Bad checksum"); 179 } 180 } 181 182 183 /** 184 * Read a record from a hex file and verify the checksum. 185 * 186 * @param f the hex file 187 * @throws IOException 188 */ 189 private void readRecord(HexFile f) throws IOException { 190 try { 191 startRecord(f); 192 readHeader(f); 193 readData(f); 194 checkRecord(f); 195 } catch (IOException e) { 196 throw new IOException(e); 197 } 198 valid = true; 199 } 200 201 202 private static final Logger log = LoggerFactory.getLogger(HexRecord.class); 203 204}