def toByteCode(self): bytes = super(self.__class__, self).toByteCode() coded_modules = [codec.VALUE_TO_UNITCODE_MAP[i] for i in self.modules] modmask = utils.unmerge_bytes(utils.numbers_to_mask(coded_modules), 2) bytes.extend(modmask) return bytes
def toByteCode(self): """Convert the timers, macro initiators, macros, DST data, sunrise/sunset data and everything else described by this class into bytecode suitable for uploading to the CM15.""" mem = utils.MemoryBuffer(capacity=0x2000) assert(0 == (self.sunrise_sunset_resolution >> 8)) mem[4] = self.sunrise_sunset_resolution & 0xFF bytes = codec.DST_DAYS.encode(self.dst_data) mem.setFromByteArray(5, bytes) tranceivedHousecodeMap = dict([(k, 1) for k in self.tranceivedHousecodes]) for i in range(0,16): c = chr(ord('A')+i) if c not in tranceivedHousecodeMap: tranceivedHousecodeMap[c] = 0 mem.setFromByteArray(9, codec.HOUSECODE_MASK.encode(tranceivedHousecodeMap)) # TODO: data between 0x0b and 0x18 not currently understood ptr = 0x18 # the eeprom download seems to contain a lot of single bytes holding # the least-significant 8 bits of their address. Presumably this # was just a default value fill of the buffer, although these # markers do make the rom code a bit easier for a human to read def setLowOrderAddressByte(): mem[ptr] = ptr & 0xFF return ptr + 1 ptr = setLowOrderAddressByte() # write the timer initiators. Timers refer to start and stop macro # chains, but the addresses of the macros are indirected through a # set of 2-byte pointers stored in memory after the timer # initiators. My guess is that this is to get around the 10-bit # address limitation of the codec, as this scheme allows macros to # be placed anywhere in the 16-bit address space while also # allowing much of the original eeprom hardware from the CM12 to # be reused. indirect_table = dict() indirect_table_offset = ptr + (len(self.timer_initiators) * codec.TIMER_INITIATOR.codelength) indirect_table_offset = utils.alignToBoundary(indirect_table_offset, 4) for t in self.timer_initiators: hasMacroId = False for k in ("start_macro_id", "stop_macro_id"): if k in vars(t): hasMacroId = True id = vars(t)[k] indirectPtr = indirect_table.get(id) if indirectPtr is None: offset = indirect_table_offset + (2 * len(indirect_table)) indirectPtr = indirect_table[id] = offset t.start_macro_ptr = indirectPtr assert(hasMacroId) bytes = t.toByteCode() ptr = mem.setFromByteArray(ptr, bytes) ptr = setLowOrderAddressByte() # A few bytes here which are not well understood - seems to # contain address boundary of the macros: ptr = mem.setFromByteArray(ptr, [0x00]) ptr = setLowOrderAddressByte() macro_limits_offset = ptr # need to write the macro end address here ptr += 2 # now set the offset at the start of the eeprom macro_initiator_table_offset = ptr + (3 * len(indirect_table)) mem.setFromByteArray(0, utils.unmerge_bytes(macro_initiator_table_offset, 2)) # write the macro initiators: ptr = macro_initiator_table_offset for m in self.macro_initiators: ptr = setLowOrderAddressByte() ptr = mem.setFromByteArray(ptr, m.toByteCode()) ptr = setLowOrderAddressByte() ptr = mem.setFromByteArray(ptr, [0xff, 0xff, 0xff]) # now write the actual macro chains: for m in self.macro_chains: ptr = setLowOrderAddressByte() ptr = mem.setFromByteArray(ptr, m.toByteCode()) # go back and write the end address: end_of_macros_address = ptr mem.setFromByteArray(macro_limits_offset, utils.unmerge_bytes(end_of_macros_address, 2)) ptr = setLowOrderAddressByte() ptr = mem.setFromByteArray(ptr, [0x00]) # fill the rest of this row and the next with 0xffs: fill_address = utils.alignToBoundary(ptr, 16) fill_address += 16 ptr = mem.setFromByteArray(ptr, [0xff for i in range(ptr, fill_address)]) # now write the sunrise/sunset times: ptr = len(mem) - 12 for sunrise_sunset_time in reversed(self.sunrise_sunset_times): ptr -= codec.DAWN_DUSK_ENTRY.codelength mem.setFromByteArray(ptr, codec.DAWN_DUSK_ENTRY.encode(sunrise_sunset_time)) # write this offset into byte 2: start_of_sunrise_sunset_table = ptr mem.setFromByteArray(2, utils.unmerge_bytes(start_of_sunrise_sunset_table, 2)) # TODO: 12 bytes at the end of the eeprom not currently understood return mem
def toByteCode(self): bytes = super(self.__class__, self).toByteCode() flagmask = utils.unmerge_bytes(utils.numbers_to_mask(self.flags), 2) bytes.extend(flagmask) return bytes