mmiogrok/dvf/dvfparse.py

302 lines
9.0 KiB
Python

from __future__ import annotations
import argparse, os, os.path, struct, sys
from typing import *
def name2int(s: Union[str, bytes]):
if isinstance(s, str): return ord(s[0]) + ord(s[1])*256
else:
assert isinstance(s, bytes)
return s[0] + s[1]*256
# supported section types:
# * SN: SFRs
# * VN: vectors
# * MI: memory layout
# * ES: 2nd SFR
# * MD: probably "metadata", version & timestam
#
# can be parsed:
# * SC: something with SFRs
# * FL: flash stuff
# * OD: debug stuff
#
# I know about their existence:
# * CG, XD, CT, QB, EV, EM, BS, UE, PC
#
# I have no clue:
# * VI, MC, SS, IO, PT, MK, MT, ID, BI, LD, PL, PI, SI, CI, SX, PE, PS
class DvfSN(NamedTuple):
name: str
flg0: int
flg1: int
flg2: int
address: int
flg3: int
flg4: int
def parse(b: bytes) -> 'DvfSN':
name, flg0, flg1, flg2, addr, nul1, flg3, flg4, nulls = struct.unpack(
'<16sH2HHH2H10s', b)
name = name.rstrip(b'\0').decode('utf-8')
assert nul1 == 0
assert all(n == 0 for n in nulls)
return DvfSN(name, flg0, flg1, flg2, addr, flg3, flg4)
DvfES = DvfSN
class DvfVN(NamedTuple):
name: str
address: int
int_type: int
enable_reg_addr: int
enable_bit_flag: int
def parse(b: bytes) -> DvfVN:
name, addr, ityp, enrg, enbt = struct.unpack('<16sHHHH', b)
name = name.rstrip(b'\0').decode('utf-8')
return DvfVN(name, addr, ityp, enrg, enbt)
class DvfMI(NamedTuple):
ACCESS_R = 1
ACCESS_W = 2
ACCESS_X = 4
ACCESS_RSV = 8 # reserved
MREG_FLASH = 0x00
MREG_RAM = 0x01
# TODO: rama again? (0x2)
# TODO: 0..ffffe, 0..fffff regions (0xb, 0xc)
MREG_RESERVED = 0x0e
MREG_CPUREGS = 0x0f
MREG_SFR = 0x10
# TODO ffd0..ffdf (0x11)
MREG_VECTOR = 0x12
MREG_CALLT = 0x13
MREG_CALLF = 0x14
MREG_SHORT = 0x16 # "short direct addressing" in instruction set
MREG_SHORT_SFR = 0x18 # above + is SFR
MREG_SFR2 = 0x24
MREG_ROM_ENTRY = 0x25
MREG_MIRROR = 0x26
MREG_PRIVRAM = 0x27
MREG_ROM = 0x28
addr_start: int
addr_end: int
mreg_type: int
access: int
def parse(b: bytes) -> DvfMI:
start, end, typ, acc, zeros = struct.unpack('<IIHHI', b)
assert zeros == 0
return DvfMI(start, end, typ, acc)
class DvfMD(NamedTuple):
version: str # file format version?
timestamp: str # also includes author
def parse(b: bytes) -> DvfMD:
assert len(b) >= 40
ver, ts = b[:8], b[8:]
ver = ver.rstrip(b'\0').decode('utf-8')
ts = ts.rstrip( b'\0').decode('utf-8')
return DvfMD(ver, ts)
class DvfSC(NamedTuple):
name: str
addr1: int
addr2: int
def parse(b: bytes) -> DvfSC:
name, a1, a2 = struct.unpack('<16sII', b)
name = name.rstrip(b'\0').decode('utf-8')
return DvfSC(name, a1, a2)
class DvfFL(NamedTuple):
FL_TYPE_NBLOCKS = 1
FL_TYPE_BLOCK = 2
fl_type: int
index: int
address: int
extra: int
def parse(b: bytes) -> DvfFL:
typ, ind, addr, ex = struct.unpack('<HHHH', b)
return DvfFL(typ, ind, addr, ex)
class DvfOD(NamedTuple):
index: int
subindex: int
value: int
def parse(b: bytes) -> DvfOD:
ind, subind, val = struct.unpack('<HHI', b)
return DvfOD(ind, subind, val)
class DvfSect(NamedTuple):
SN = name2int("SN")
VN = name2int("VN")
MI = name2int("MI")
ES = name2int("ES")
MD = name2int("MD")
SC = name2int("SC")
FL = name2int("FL")
OD = name2int("OD")
def ENTSIZE(dvftype: int) -> Dict[int, int]:
return {
name2int(b'SN'):40,
name2int(b'VN'):24,
name2int(b'MI'):16,
name2int(b'CG'):80,
name2int(b'ES'):40, # 78K0R only?
name2int(b'XD'):12, # 78K0R only?
name2int(b'CT'):96,
name2int(b'SC'):24,
name2int(b'QB'):136 if (dvftype & 0xF0) == 0x10 else 108,
name2int(b'EV'):8,
name2int(b'FL'):8,
name2int(b'OD'):8,
name2int(b'AF'):16,
name2int(b'SD'):16,
name2int(b'EM'):12,
name2int(b'MD'):48,
name2int(b'BS'):16, # 78K0 only?
name2int(b'UE'):12, # RL78 only?
name2int(b'PC'):20, # idk
}
TYPES = {
name2int('SN'): DvfSN,
name2int('ES'): DvfES,
name2int('VN'): DvfVN,
name2int('MI'): DvfMI,
name2int('MD'): DvfMD,
name2int('SC'): DvfSC,
name2int('FL'): DvfFL,
name2int('OD'): DvfOD
}
name: int
nentries: int
offset: int
class Dvf(NamedTuple):
MAGIC = 0x1a
# TODO: add support for V850/RH850, RX
MCU_78K0 = 0
MCU_78K0R = 0x10
MCU_RL78x1y = 0x20
MCU_RL78x2y = 0x30
IDK_KNOWN_78K0 = {
b'\0\x001\x02!\x02\0\0H\x11', # Kx2
b'\0\x001\x02\0\x01\0\0H\x11', # Kx2-A, uPD78F8039
b'\0\x001\x02\x01\x01\0\0H\x11', # Fx2
b'\0\x001\x02\x01\x02\0\0H\x11', # Kx2-L
b'\0\x001\x02\x10\x01\0\0H\x11', # misc (Ix2, KC2-A, uPD78F0730)
b'\0\x001\x02\x11\x01\0\0H\x11', # FC2
b'\0\x001\x02\x12\x01\0\0H\x11', # Lx3
b'\0\x002\x02\0\x01\0\0H\x11', # misc (uPD78F8077, Dx2, Kx2-C, uPD78F8039)
b'\0\x002\x02\x02\x01\0\0H\x11', # Fx2-L
b'\0\x002\x02\x10\x01\0\0H\x11', # Lx3-M
}
IDK_KNOWN_78K0R = {
b'\x0b\0\0\x03 \x02\0\0H\x11', # KC3-L
b'\x0b\0\0\x03!\x01\0\0H\x11', # Lx3
b'\x0b\0\0\x03\0\x01\0\0H\x11', # misc (Kx3-L(USB), Hx3, LG3-M, uPD78F80xx)
b'\x0b\0\0\x03\0\x02\0\0H\x11', # Kx3-L, Ix3
b'\x0b\0\0\x03\0\x03\0\0H\x11', # Kx3
b'\x0b\0\0\x03\x01\x01\0\0H\x11', # Fx3, Kx3-C
b'\x0b\0\0\x03\x01\x02\0\0H\x11', # Kx3-L
b'\x0b\0\0\x03\x10\x01\0\0H\x11', # Lx3, Kx3-A
b'\x0b\0\0\x03\x10\x02\0\0H\x11', # Kx3-L
b'\x0b\0\0\x03\x11\x01\0\0H\x11', # KF3-C
}
IDK_KNOWN_RL78x1y = {
b'\x0c\0\0\x03 \x01\0\0H\x11', # G11, G1D
b'\x0c\0\0\x03!\x01\0\0H\x11', # I1A, L1C
b'\x0c\0\0\x030\x02\0\0H\x11', # G14
b'\x0c\0\0\x03\0\x01\0\0H\x11', # misc (G13A, G1H, F15, I1E, H1D, I1C)
b'\x0c\0\0\x03\x01\x01\0\0H\x11', # misc (L1A, I1D, G1G, G1A, L13, I1C, D1A, G1F, F1A)
b'\x0c\0\0\x03\x01\x02\0\0H\x11', # G1E
b'\x0c\0\0\x03\x02\x01\0\0H\x11', # I1B, F12
b'\x0c\0\0\x03\x03\x01\0\0H\x11', # G10, L12
b'\x0c\0\0\x03\x10\x01\0\0H\x11', # misc (G1N, F1E, G1P, G1C, D1A, G1M, G1K)
b'\x0c\0\0\x03\x11\x01\0\0H\x11', # L1C, F13, F14
b'\x0c\0\0\x03\x12\x01\0\0H\x11', # G12
b'\x0c\0\0\x03\x14\x01\0\0H\x11', # G13
}
IDK_KNOWN_RL78x2y = {
b'\x0c\0 \x03\0\x01\0\0H\x11', # F24
b'\x0c\0\0\x030\x02\0\0H\x11', # FGIC
b'\x0c\0\0\x03\x02\x01\0\0H\x11', # FGIC
b'\x0c\0\x10\x03\x10\x01\0\0H\x11', # G23
}
magic: int
copyright_notice: str
idk: bytes
chipname_disp: str
chipname_sym: str
mcutype: int
sections: Dict[int, Any]
sections_raw: List[DvfSect]
@staticmethod
def parse(data: bytes, mcutype = None) -> Dvf:
magic, crnot, idk, name_disp, name_sym, nsect = struct.unpack(
'<B35s16s16s8sI', data[:80])
assert magic == Dvf.MAGIC
assert idk[14:16] in { b"\x02\0", b"\x03\0", b"\x04\0" }, str(idk)
crnot = crnot.rstrip(b'\0').decode('utf-8')
name_disp = name_disp.rstrip(b'\0').decode('utf-8')
name_sym = name_sym.rstrip(b'\0').decode('utf-8')
#mcutype = None
if mcutype is None:
idkfix = idk[0:10]
if idkfix in Dvf.IDK_KNOWN_78K0: mcutype = Dvf.MCU_78K0
elif idkfix in Dvf.IDK_KNOWN_78K0R: mcutype = Dvf.MCU_78K0R
elif idkfix in Dvf.IDK_KNOWN_RL78x1y: mcutype = Dvf.MCU_RL78x1y
elif idkfix in Dvf.IDK_KNOWN_RL78x2y: mcutype = Dvf.MCU_RL78x2y
else: assert False, ("unknown MCU type", idk)
sects = {}
sraw = []
for i in range(nsect):
shblob = data[(80+i*12):(80+(i+1)*12)]
name, nentries, offset, zeros = struct.unpack('<HHH6s', shblob)
if not all(z == 0 for z in zeros):
continue # idk
sraw.append(DvfSect(name, nentries, offset))
if nentries == 0: continue
#print("%c%c" % (chr(name&0xff),chr(name>>8)))
entsize = DvfSect.ENTSIZE(mcutype)[name]
sdblob = data[offset:(offset+entsize*nentries)]
styp = DvfSect.TYPES.get(name)
if styp is None: sects[name] = sdblob
else:
l = []
for j in range(nentries):
sdiblob = sdblob[(j*entsize):((j+1)*entsize)]
#if name == name2int("MD"):
# print("esz",entsize,"j",j,"blob",sdiblob)
l.append(DvfSect.TYPES[name].parse(sdiblob))
sects[name] = l
return Dvf(magic, crnot, idk, name_disp, name_sym, mcutype, sects, sraw)