#!/usr/bin/python
# -*- coding: iso-8859-15 -*-
import intelhex
program_is_depressed = False
class ProgramContentsError(Exception):
pass
class RelocatePIC18(object):
def __init__(self, device):
self.device = device
def _make_goto(self, target):
if target&1 == 1:
raise ValueError("Goto target must be even: 0x%X"%target)
target >>= 1
if target > 0xFFFFF:
raise ValueError("Target too large for goto: 0x%X"%target)
w0 = 0xEF00 | ((target)&0xFF)
w1 = 0xF000 | (target>>8)
return [w0, w1]
# we consider goto and call here
def _is_goto(self, word):
return word & 0xFF00 in (0xEC00,0xED00,0xEF00)
def _get_goto_target(self, words):
if not self._is_goto(words[0]):
raise ValueError("Instruction is not a goto derivative: 0x%04X"%words[0])
if words[1] & 0xF000 != 0xF000:
raise ValueError("Goto derivative has invalid second word: 0x%04X"%(words[1]))
return ((words[0] & 0x00FF) | ((words[1]&0x0FFF)<<8))<<1
def _patch_goto(self, words, target):
self._get_goto_target(words) #validate
w0 = words[0] & 0xFF00
w1 = words[1] & 0xF000
if target&1 == 1:
raise ValueError("Goto target must be even: 0x%X"%target)
target >>= 1
if target > 0xFFFFF:
raise ValueError("Target too large for goto: 0x%X"%target)
w0 |= ((target)&0xFF)
w1 |= (target>>8)
return [w0, w1]
# convert a 2's-complement number to a python int
def _sign_extend(self, num, signmask):
base = num & (~signmask)
sign = num & signmask == signmask
if sign:
return base - signmask
return base
# note: we consider rcall to be a subtype of bra for our purposes
def _is_bra(self, word):
return word & 0xF800 in (0xE000,0xD000,0xD800)
def _get_bra_target(self, word, address):
if word & 0xF800 == 0xE000:
n = self._sign_extend(word & 0x00FF,0x0080)
elif word & 0xF800 in (0xD000,0xD800):
n = self._sign_extend(word & 0x07FF,0x0400)
else:
raise ValueError("Instruction is not a branch derivative: 0x%04X"%word)
return address + 2 * n + 2
def _make_bra(self, target, template, address):
if address & 1 == 1:
raise ValueError("Branch address must be even: 0x%X"%address)
if target & 1 == 1:
raise ValueError("Branch target must be even: 0x%X"%target)
n = (target - address - 2)/2
if template & 0xF800 == 0xE000:
if not -0x80 <= n < 0x80:
raise ValueError("Branch target out of range: 0x%X (n=0x%Xtemplate=0x%04X)"%(target,n,template))
return (template & 0xFF00) | (n & 0x00FF)
elif template & 0xF800 in (0xD000,0xD800):
if not -0x400 <= n < 0x400:
raise ValueError("Branch target out of range: 0x%X (n=0x%X,template=0x%04X)"%(target,n,template))
return (template & 0xF800) | (n & 0x07FF)
else:
raise ValueError("Template must be a branch derivative: 0x%04X"%word)
def _relocate_target_if_needed(self, target):
if self.device.reset_vector <= target < self.device.reset_vector+self.device.reset_vector_size:
target = target - self.device.reset_vector + self.device.relocation_reset
if self.do_interrupt:
if self.device.interrupt_vector <= target < self.device.interrupt_vector+self.device.interrupt_vector_size:
target = target - self.device.interrupt_vector + self.device.relocation_interrupt
return target
def _relocate_vector(self, data16, start, size, target, patchtarget = None):
# sigh (note that this is sort of fixed now - unset program_is_depressed)
if program_is_depressed and self._is_goto(data16[start+size-self.device.word_size]):
print """
Hi there, it's me, the bootloader program.
You know what, I'm sick of this.
Coding around random crap when patching the vectors is a pain in the rear.
Now you have a goto in the middle of the end boundary of the vector.
Do you even know what you've done?
It's people like you that create the world's problems.
Yeah, I could replace it with a branch and put it in the branch table.
You know what? I don't care.
What was supposed to be a simple ten-line relocation has turned into a 250-line monster.
That's not counting the constant database in devices.py.
I'm depressed now. It's 7am and I'm still working on this.
I'm raising an exception now.
You get to fix this one.
See you later.
Hola, soy yo, el bootloader.
¿Sabes que? Estoy harto de todo esto.
Escribir código para controlar todos estos casos raros cuando parcheo los vectores es un coñazo.
Ahora tienes un goto en medio del borde del vector.
¿Acaso sabes lo que has hecho?
La gente como tu sois los que causais los problemas de este mundo.
Si, si, podria reemplazarlo por un branch y ponerlo en la tabla.
¿Sabes que? No me importa.
Esto se suponía que iba a ser una tontería de 10 lineas, y ahora es un monstruo de 250.
Eso sin contar la base de datos de constantes en devices.py.
Ahora estoy deprimido. Son las 7am y todavía ando trabajando en esto.
Voy a lanzar una excepción ahora.
Te toca a ti arreglar el problema esta vez.
Nos vemos.
"""
raise Exception("Program is depressed")
# copy vector
for addr in xrange(0,size,2):
data16[target+addr] = data16[start+addr]
r_end = target+size
gs = self.device.goto_size
ws = self.device.word_size
gws = gs//ws
# add continuation gotos
data16[r_end : r_end+gs] = self._make_goto(self._relocate_target_if_needed(start+size))
data16[r_end+gs : r_end+2*gs] = self._make_goto(self._relocate_target_if_needed(start+size+ws))
# fix embedded gotos in target vector
for addr in xrange(0,size-ws,2):
if self._is_goto(data16[target+addr]):
old_target = self._get_goto_target(data16[target+addr:target+addr+gs])
new_target = self._relocate_target_if_needed(old_target)
if old_target != new_target:
data16[target+addr:target+addr+gs] = self._patch_goto(data16[target+addr:target+addr+gs], new_target)
if patchtarget != None:
bt = self.device.relocation_branch_table
bte = self.device.relocation_branch_table_entries
# add entries to branch table and patch target vector
for addr in xrange(0,size,2):
if self._is_bra(data16[start+addr]):
tg = self._get_bra_target(data16[start+addr],start+addr)
tg = self._relocate_target_if_needed(tg)
try:
data16[target+addr] = self._make_bra(tg,data16[start+addr],target+addr)
except ValueError,e:
if not "out of range" in e.args[0]:
raise e
if self.bti >= bte:
raise ProgramContentsError("Too many branches in vectors (max %d)"%bte)
data16[bt+self.bti*gs:bt+self.bti*gs+gs] = self._make_goto(tg)
data16[target+addr] = self._make_bra(bt+self.bti*gs,data16[start+addr],target+addr)
self.bti += 1
# fix a possible goto at the end of the source using the branch table
if self._is_goto(data16[start+size-ws]):
word = data16[start+size-ws] & 0xFF00
if word == 0xEC00:
bra_tpl = 0xD800
elif word == 0xED00:
bra_tpl = None
elif word == 0xEF00:
bra_tpl = 0xD000
tg = self._get_goto_target(data16[start+size-ws:start+size-ws+gs])
tg = self._relocate_target_if_needed(tg)
if bra_tpl != None:
try:
data16[target+size-ws] = self._make_bra(tg,bra_tpl,target+size-ws)
except ValueError,e:
if not "out of range" in e.args[0]:
raise e
if self.bti >= bte:
raise ProgramContentsError("Too many branches in vectors (max %d) (this branch autogenerated due to broken goto)"%bte)
data16[bt+self.bti*gs:bt+self.bti*gs+gs] = self._patch_goto(data16[start+size-ws:start+size-ws+gs],tg)
data16[target+size-ws] = self._make_bra(bt+self.bti*gs,0xD000,target+size-ws)
self.bti += 1
else:
if self.bti >= bte:
raise ProgramContentsError("Too many branches in vectors (max %d)"%bte)
data16[bt+self.bti*gs:bt+self.bti*gs+gs] = self._patch_goto(data16[start+size-ws:start+size-ws+gs],tg)
data16[target+size-ws] = self._make_bra(bt+self.bti*gs,0xD000,target+size-ws)
self.bti += 1
# fix embedded gotos in original vector too
for addr in xrange(0,size,2):
if self._is_goto(data16[start+addr]):
old_target = self._get_goto_target(data16[start+addr:start+addr+gs])
new_target = self._relocate_target_if_needed(old_target)
if old_target != new_target:
data16[start+addr:start+addr+gs] = self._patch_goto(data16[start+addr:start+addr+gs], new_target)
# finally, patch original vector
data16[start:start+gs] = self._make_goto(patchtarget)
def relocate(self, data, do_interrupt = False):
data16 = intelhex.IntelHex16bit(data)
data16.padding = 0x0000 #nop
self.bti = 0
self.do_interrupt = do_interrupt
# relocate reset vector
self._relocate_vector(data16,
self.device.reset_vector,
self.device.reset_vector_size,
self.device.relocation_reset,
self.device.bootloader_reset)
patch_interrupt = None
if do_interrupt:
patch_interrupt = self.device.bootloader_interrupt
# relocate interrupt vector
self._relocate_vector(data16,
self.device.interrupt_vector,
self.device.interrupt_vector_size,
self.device.relocation_interrupt,
patch_interrupt)