0
0

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?

PIC18F26Q84のディスアセンブラ

Last updated at Posted at 2025-02-27

前のやつの使いまわしです

マルチwordの命令と拡張IntelHexに対応しました
prepare内を変更することで12f683も対応出来ます

#!/usr/bin/env python3

import sys
import os
from functools import cmp_to_key

KB = 1024

init_opcode = None
INSTRUCTION_SIZE = 14
program_size = 16 * KB


def prepare():
    global init_opcode
    global INSTRUCTION_SIZE
    global program_size

    # init_opcode = init_opcode12f683
    init_opcode = init_opcode18f26q84

    # INSTRUCTION_SIZE = 14     # 12f683
    INSTRUCTION_SIZE = 16       # 18F26Q84

    # program_size = 16 * KB    # 12f683
    program_size = 128 * KB   # 18F26Q84


def usage():
    explain = """usage:
    ./disas.py <file>
"""
    print(explain, end="")


def get_file_path():
    if len(sys.argv) != 2:
        return None
    file_path = sys.argv[1]
    if not os.path.isfile(file_path):
        return None
    return file_path


class HexRecord:
    def __init__(self, address, data):
        self.address = address
        self.data = (data[1] << 8) | data[0]

    def __str__(self):
        data_str = "{:04x}".format(self.data)
        return f"{self.address:04X}: {data_str}"


class IntelHexParser:
    def __init__(self, filepath):
        self.filepath = filepath
        self.records = []

    def get_extend_addr(self, data) -> int:
        extend = 0
        if len(data) == 2:
            extend = (data[0] << 8 | data[1]) << 16
        return extend

    def parse(self):
        last_data = None
        extend_addr = 0
        with open(self.filepath, "r") as file:
            for line in file:
                if not line.startswith(":"):
                    continue

                byte_count = int(line[1:3], 16)
                address = int(line[3:7], 16)
                address = address + extend_addr
                record_type = int(line[7:9], 16)
                data = [
                    int(line[i : i + 2], 16) for i in range(9, 9 + byte_count * 2, 2)
                ]

                if last_data:
                    address = address - 1
                    byte_count = byte_count + 1
                    data = data.append(last_data)
                    last_data = None

                # データレコードのみ追加
                if record_type != 0x00:
                    if record_type == 0x04:
                        extend_addr = self.get_extend_addr(data)
                    continue
                if address > program_size:
                    continue
                if (byte_count % 2) != 0:
                    last_data = data.pop()
                    byte_count = byte_count - 1

                pairs = list(zip(data[::2], data[1::2]))

                for x in pairs:
                    record = HexRecord(address, x)
                    self.records.append(record)
                    address = address + 2

    def get_records(self):
        return self.records


class Instruction:
    disas_str = ""

    def __init__(
        self, mnemonic, operands, description, bits, size=1, prev=None, next=None
    ):
        self.mnemonic = mnemonic
        self.operands = operands
        self.description = description
        self.bits = bits
        self.size = size
        self.prev = prev
        self.next = next

    def __str__(self):
        p = f"p:{self.prev.mnemonic}" if self.prev is not None else ""
        n = f"n:{self.next.mnemonic}" if self.next is not None else ""

        return f"{self.mnemonic}({self.size}) {self.description} {self.bits} {p} {n}"

    def __repr__(self):
        return self.__str__()

    def disas(self, i, data) -> int:
        if self.size == 1:
            # print(f"size1:{self.size} {data[i].data:x} {self.operands} {self.bits}")
            desc = self.check_operands(data[i].data, self.operands, self.bits)
            multiplier = 2 if self.mnemonic in ("GOTO", "CALL") else 1
            desc_str = ",".join([f"{k}: {v * multiplier:X}" for k, v in desc.items()])
            # print(f"desc:{desc_str}")
            self.disas_str = (
                f"{self.mnemonic} {desc_str} {self.description} {self.bits}"
            )
        else:
            size = self.get_size()
            if size != self.size:
                raise Exception(
                    f"size not much {data[i].address:x}: {data[i].data:x} {size},{self.size}\n{self}"
                )

            inst = self
            bits = ""
            operands = {}
            shift = {}
            for j in range(size):
                desc = self.check_operands(data[i + j].data, inst.operands, inst.bits)
                for k, v in desc.items():
                    sh = shift.get(k, 0)
                    operands[k] = operands.get(k, 0) + (v << sh)
                    shift[k] = sh + inst.bits.count(k)

                bits = f"{bits} {inst.bits}"
                inst = inst.next
                # desc_str = ",".join([f"{k}: {v:X}" for k, v in desc.items()])
                # print(f"#### desc:{desc_str}")

            multiplier = 2 if self.mnemonic in ("GOTO", "CALL") else 1
            desc_str = ",".join([f"{k}: {v * multiplier:X}" for k, v in operands.items()])

            self.disas_str = f"{self.mnemonic} {desc_str} {self.description} {bits}"
            # if data[i].data == 0xef13:
            #     raise Exception(f"stop {self}")
        return self.size - 1

    def get_size(self):
        inst = self
        count = 1
        while inst.next:
            inst = inst.next
            count = count + 1
        return count

    def check_operands(self, value: int, operands: str, bit_mask: str) -> dict:
        value_bits = f"{value:0{INSTRUCTION_SIZE}b}"

        operand_list = operands.split(", ")

        result = {}

        # print(f"list:{operand_list} value:{value_bits}")
        for operand in operand_list:
            operand_bits = []

            if operand not in bit_mask:
                break

            l = len(bit_mask)
            if len(value_bits) != l:
                print(f"#### Error v:{value_bits} m:{bit_mask}")
                break

            bit_index = 0
            found = False
            for i in range(l):
                if operand == bit_mask[i]:
                    operand_bits.append(value_bits[i])
                    found = True
                else:
                    if found:
                        break
                    continue

                bit_index += 1

            # print(operand_bits)
            if operand_bits:
                result[operand] = int("".join(operand_bits), 2)

        return result


_instructions12f683 = [
    "ADDWF	f, d	Add W and f	00	0111	dfff	ffff",
    "ANDWF	f, d	AND W with f	00	0101	dfff	ffff",
    "CLRF	f	Clear f	00	0001	lfff	ffff",
    "CLRW	–	Clear W	00	0001	0xxx	xxxx",
    "COMF	f, d	Complement f	00	1001	dfff	ffff",
    "DECF	f, d	Decrement f	00	0011	dfff	ffff",
    "DECFS	f, dZ	Decrement f, Skip if 0	00	1011	dfff	ffff",
    "INCF	f, d	Increment f	00	1010	dfff	ffff",
    "INCFS	f, dZ	Increment f, Skip if 0	00	1111	dfff	ffff",
    "IORWF	f, d	Inclusive OR W with f	00	0100	dfff	ffff",
    "MOVF	f, d	Move f	00	1000	dfff	ffff",
    "MOVWF	f	Move W to f	00	0000	lfff	ffff",
    "NOP	–	No Operation	00	0000	0xx0	0000",
    "RLF	f, d	Rotate Left f through Carry	00	1101	dfff	ffff",
    "RRF	f, d	Rotate Right f through Carry	00	1100	dfff	ffff",
    "SUBWF	f, d	Subtract W from f	00	0010	dfff	ffff",
    "SWAPF	f, d	Swap nibbles in f	00	1110	dfff	ffff",
    "XORWF	f, d	Exclusive OR W with f	00	0110	dfff	ffff",
    "BCF	f, b	Bit Clear f	01	00bb	bfff	ffff",
    "BSF	f, b	Bit Set f	01	01bb	bfff	ffff",
    "BTFSC	f, b	Bit Test f, Skip if Clear	01	10bb	bfff	ffff",
    "BTFSS	f, b	Bit Test f, Skip if Set	01	11bb	bfff	ffff",
    "ADDLW	k	Add literal and W	11	111x	kkkk	kkkk",
    "ANDLW	k	AND literal with W	11	1001	kkkk	kkkk",
    "CALL	k	Call Subroutine	10	0kkk	kkkk	kkkk",
    "CLRWDT	–	Clear Watchdog Timer	00	0000	0110	0100",
    "GOTO	k	Go to address	10	1kkk	kkkk	kkkk",
    "IORLW	k	Inclusive OR literal with W	11	1000	kkkk	kkkk",
    "MOVLW	k	Move literal to W	11	00xx	kkkk	kkkk",
    "RETFIE	–	Return from interrupt	00	0000	0000	1001",
    "RETLW	k	Return with literal in W	11	01xx	kkkk	kkkk",
    "RETURN	–	Return from Subroutine	00	0000	0000	1000",
    "SLEEP	–	Go into Standby mode	00	0000	0110	0011",
    "SUBLW	k	Subtract W from literal	11	110x	kkkk	kkkk",
    "XORLW	k	Exclusive OR literal with W	11	1010	kkkk	kkkk",
]

_instructions18f26q84 = [
    # Mnemonic	Operands	説明	p	word	サイクル	16ビット命令ワード	影響を受けるステータス	備考"
    "ADDWF	f, d, a	WREG と f を加算		1	1	0010 01da ffff ffff	C, DC, Z, OV, N	1",
    "ADDWFC	f, d, a	WREG とキャリービットを f に加算		1	1	0010 00da ffff ffff	C, DC, Z, OV, N	1",
    "ANDWF	f, d, a	WREG と f を AND		1	1	0001 01da ffff ffff	Z, N	1",
    "CLRF	f, a	f をクリア		1	1	0110 101a ffff ffff	Z	",
    "COMF	f, d, a	f を反転		1	1	0001 11da ffff ffff	Z, N	1",
    "DECF	f, d, a	f をデクリメント		1	1	0000 01da ffff ffff	C, DC, Z, OV, N	1",
    "INCF	f, d, a	f をインクリメント		1	1	0010 10da ffff ffff	C, DC, Z, OV, N	1",
    "IORWF	f, d, a	WREG と f を OR		1	1	0001 00da ffff ffff	Z, N	1",
    "MOVF	f, d, a	f を WREG または f に移動		1	1	0101 00da ffff ffff	Z, N	1",
    "MOVFF	s, d	12ビット fs を 12ビット fd に移動	1	2	2	1100 ssss ssss ssss	なし	1, 3, 4",
    "MOVFF	s, d	12ビット fs を 12ビット fd に移動	2	2	2	1111 dddd dddd dddd	なし	1, 3, 4",
    "MOVFFL	s, d	Move fs (14-bit source) to fd (14-bit destination)	1	3	3	0000 0000 0110 ssss	なし	1, 3",
    "MOVFFL	s, d	Move fs (14-bit source) to fd (14-bit destination)	2	3	3	1111 ssss ssss ssdd	なし	1, 3",
    "MOVFFL	s, d	Move fs (14-bit source) to fd (14-bit destination)	3	3	3	1111 dddd dddd dddd	なし	1, 3",
    "MOVWF	f, a	WREG を f に移動		1	1	0110 111a ffff ffff	なし	",
    "MULWF	f, a	WREG と f を乗算		1	1	0000 001a ffff ffff	なし	1",
    "NEGF	f, a	f を負数に変換		1	1	0110 110a ffff ffff	C, DC, Z, OV, N	1",
    "RLCF	f, d, a	f を左ローテート (キャリー経由)		1	1	0011 01da ffff ffff	C, Z, N	1",
    "RLNCF	f, d, a	f を左ローテート (キャリーなし)		1	1	0100 01da ffff ffff	Z, N	1",
    "RRCF	f, d, a	f を右ローテート (キャリー経由)		1	1	0011 00da ffff ffff	C, Z, N	1",
    "RRNCF	f, d, a	f を右ローテート (キャリーなし)		1	1	0100 00da ffff ffff	Z, N	1",
    "SETF	f, a	f をセット		1	1	0110 100a ffff ffff	なし	",
    "SUBFWB	f, d, a	Subtract f from WREG with Borrow		1	1	0101 01da ffff ffff	C, DC, Z, OV, N	1",
    "SUBWF	f, d, a	Subtract WREG from f		1	1	0101 11da ffff ffff	C, DC, Z, OV, N	1",
    "SUBWFB	f, d, a	Subtract WREG from f with Borrow		1	1	0101 10da ffff ffff	C, DC, Z, OV, N	1",
    "SWAPF	f, d, a	Swap nibbles in f		1	1	0011 10da ffff ffff	None	1",
    "XORWF	f, d, a	Exclusive OR WREG with f		1	1	0001 10da ffff ffff	Z, N	1",
    "CPFSEQ	f, a	Compare f with WREG, skip if =		1	1 – 4	0110 001a ffff ffff	None	1, 2",
    "CPFSGT	f, a	Compare f with WREG, skip if >		1	1 – 4	0110 010a ffff ffff	None	1, 2",
    "CPFSLT	f, a	Compare f with WREG, skip if <		1	1 – 4	0110 000a ffff ffff	None	1, 2",
    "DECFSZ	f, d, a	Decrement f, Skip if 0		1	1 – 4	0010 11da ffff ffff	None	1, 2",
    "DCFSNZ	f, d, a	Decrement f, Skip if Not 0		1	1 – 4	0100 11da ffff ffff	None	1, 2",
    "INCFSZ	f, d, a	Increment f, Skip if 0		1	1 – 4	0011 11da ffff ffff	None	1, 2",
    "INFSNZ	f, d, a	Increment f, Skip if Not 0		1	1 – 4	0100 10da ffff ffff	None	1, 2",
    "TSTFSZ	f, a	Test f, skip if 0		1	1 – 4	0110 011a ffff ffff	None	1, 2",
    "BCF	f, b, a	Bit Clear f		1	1	1001 bbba ffff ffff	None	1",
    "BSF	f, b, a	Bit Set f		1	1	1000 bbba ffff ffff	None	1",
    "BTG	f, b, a	Bit Toggle f		1	1	0111 bbba ffff ffff	None	1",
    "BTFSC	f, b, a	Bit Test f, Skip if Clear		1	1 – 4	1011 bbba ffff ffff	None	1, 2",
    "BTFSS	f, b, a	Bit Test f, Skip if Set		1	1 – 4	1010 bbba ffff ffff	なし	1, 2",
    "BC	n	Branch if Carry		1	1 – 2	1110 0010 nnnn nnnn	なし	2",
    "BN	n	Branch if Negative		1	1 – 2	1110 0110 nnnn nnnn	なし	2",
    "BNC	n	Branch if Not Carry		1	1 – 2	1110 0011 nnnn nnnn	なし	2",
    "BNN	n	Branch if Not Negative		1	1 – 2	1110 0111 nnnn nnnn	なし	2",
    "BNOV	n	Branch if Not Overflow		1	1 – 2	1110 0101 nnnn nnnn	なし	2",
    "BNZ	n	Branch if Not Zero		1	1 – 2	1110 0001 nnnn nnnn	なし	2",
    "BOV	n	Branch if Overflow		1	1 – 2	1110 0100 nnnn nnnn	なし	2",
    "BRA	n	Branch Unconditionally		1	2	1101 0nnn nnnn nnnn	なし	2",
    "BZ	n	Branch if Zero		1	1 – 2	1110 0000 nnnn nnnn	なし	2",
    "CALL	k, s	Call subroutine	1	2	2	1110 110s kkkk kkkk	なし	2, 3",
    "CALL	k, s	Call subroutine	2	2	2	1111 kkkk kkkk kkkk	なし	2, 3",
    "CALLW	—	Call subroutine using WREG		1	2	0000 0000 0001 0100	なし	2",
    "GOTO	k	Go to address	1	2	2	1110 1111 kkkk kkkk	なし	3",
    "GOTO	k	Go to address	2	2	2	1111 kkkk kkkk kkkk	なし	3",
    "RCALL	n	Relative Call		1	2	1101 1nnn nnnn nnnn	なし	2",
    "RETFIE	s	Return from interrupt enable		1	2	0000 0000 0001 000s	INTCONx STAT bits	2",
    "RETLW	k	Return with literal in WREG		1	2	0000 1100 kkkk kkkk	なし	2",
    "RETURN	s	Return from Subroutine		1	2	0000 0000 0001 001s	なし	2",
    "CLRWDT	—	Clear Watchdog Timer		1	1	0000 0000 0000 0100	TO, PD	",
    "DAW	—	Decimal Adjust WREG		1	1	0000 0000 0000 0111	C	",
    "NOP	—	No Operation		1	1	0000 0000 0000 0000	なし	",
    "POP	—	Pop top of return stack (TOS)		1	1	0000 0000 0000 0110	なし	",
    "PUSH	—	Push top of return stack (TOS)		1	1	0000 0000 0000 0101	なし	",
    "RESET	—	Software device Reset		1	1	0000 0000 1111 1111	全て	",
    "SLEEP	—	Go into Standby mode		1	1	0000 0000 0000 0011	TO, PD	",
    "ADDFSR	n, k	Add FSR(fn) with literal (k)		1	1	1110 1000 nnkk kkkk	なし	",
    "ADDLW	k	Add literal and WREG		1	1	0000 1111 kkkk kkkk	C, DC, Z, OV, N	",
    "ANDLW	k	AND literal with WREG		1	1	0000 1011 kkkk kkkk	Z, N	",
    "IORLW	k	Inclusive OR literal with WREG		1	1	0000 1001 kkkk kkkk	Z, N	",
    "LFSR	n, k	Load FSR(fn) with a 14-bit literal (k)	1	2	2	1110 1110 00nn kkkk	なし	",
    "LFSR	n, k	Load FSR(fn) with a 14-bit literal (k)	2	2	2	1111 00kk kkkk kkkk	なし	",
    "MOVLB	k	Move literal to BSR<5:0>		1	1	0000 0001 00kk kkkk	なし	",
    "MOVLW	k	Move literal to WREG		1	1	0000 1110 kkkk kkkk	なし	",
    "MULLW	k	Multiply literal with WREG		1	1	0000 1101 kkkk kkkk	なし	",
    "RETLW	k	Return with literal in WREG		1	2	0000 1100 kkkk kkkk	なし	",
    "SUBFSR	n, k	Subtract literal (k) from FSR(fn)		1	1	1110 1001 nnkk kkkk	なし	",
    "SUBLW	k	Subtract WREG from literal		1	1	0000 1000 kkkk kkkk	C, DC, Z, OV, N	",
    "XORLW	k	Exclusive OR literal with WREG		1	1	0000 1010 kkkk kkkk	Z, N	",
    "TBLRD*	—	Table Read		1	2	0000 0000 0000 1000	なし	",
    "TBLRD*+	—	Table Read with post-increment		1	2	0000 0000 0000 1001	なし	",
    "TBLRD*-	—	Table Read with post-decrement		1	2	0000 0000 0000 1010	なし	",
    "TBLRD+*	—	Table Read with pre-increment		1	2	0000 0000 0000 1011	なし	",
    "TBLWT*	—	Table Write		1	2	0000 0000 0000 1100	なし	",
    "TBLWT*+	—	Table Write with post-increment		1	2	0000 0000 0000 1101	なし	",
    "TBLWT*-	—	Table Write with post-decrement		1	2	0000 0000 0000 1110	なし	",
    "TBLWT+*	—	Table Write with pre-increment		1	2	0000 0000 0000 1111	なし	",
]

instructions = []

_init_opcode = False


def custom_strcmp(a: str, b: str) -> int:
    la = sum(1 for char in a if char in {"0", "1"})
    lb = sum(1 for char in b if char in {"0", "1"})

    if la != lb:
        return lb - la
    for x, y in zip(a, b):
        if x == y:
            continue
        if x == "1":
            return -1
        if y == "1":
            return 1
        if x == "0":
            return -1
        if y == "0":
            return 1
    return 0


def compare_inst(a, b) -> int:
    val = custom_strcmp(a.bits, b.bits)
    return val


def to_int(a):
    try:
        return int(a)
    except ValueError:
        return 0


def to_int2(a, b):
    return [to_int(a), to_int(b)]


def compare_inst18f26q84(a: Instruction, b: Instruction) -> int:
    if a.next and (not a.prev):
        if b.next and (not b.prev):
            return custom_strcmp(a.bits, b.bits)
        else:
            return -1
    elif b.next and (not b.prev):
        return 1
    else:
        return custom_strcmp(a.bits, b.bits)


def init_opcode18f26q84():
    global _init_opcode
    global instructions
    prev = None
    if not _init_opcode:
        _init_opcode = True
        for inst in _instructions18f26q84:
            cols = inst.split("\t")
            iset = Instruction(
                cols[0], cols[1], cols[2], cols[6].replace(" ", ""), to_int(cols[4])
            )
            prio = to_int(cols[3])
            if prio > 0:
                if prio != 1:
                    prev.next = iset
                    iset.prev = prev
                prev = iset
            else:
                prev = None

            # print(f"add:{iset}")
            instructions.append(iset)

        instructions.sort(key=cmp_to_key(compare_inst18f26q84))
        # for inst in instructions:
        #     print(inst)


def init_opcode12f683():
    global _init_opcode
    global instructions
    if not _init_opcode:
        _init_opcode = True
        for inst in _instructions12f683:
            cols = inst.split("\t")
            iset = Instruction(cols[0], cols[1], cols[2], "".join(cols[3:]))
            instructions.append(iset)
        instructions.sort(key=cmp_to_key(compare_inst))


#        for inst in instructions:
#            print(inst)


def check_bits_match(value: int, pattern: str) -> bool:
    value_bits = f"{value:0{INSTRUCTION_SIZE}b}"

    for bit, char in zip(value_bits, pattern):
        if char == "1" and bit != "1":
            return False
        if char == "0" and bit != "0":
            return False
        if char not in "01":
            continue
    return True


def get_opcode(r) -> Instruction:
    global instructions
    global init_opcode
    init_opcode()

    for inst in instructions:
        if check_bits_match(r, inst.bits):
            return inst
    print(f"#### not found {r:x}")
    return None


def analyze(data):
    skip = 0
    for i in range(len(data)):
        if skip > 0:
            skip = skip - 1
            continue
        x = data[i]
        # print(f"{i}: {x.address:x} {x.data:x}")
        inst = get_opcode(x.data)
        skip = inst.disas(i, data)

        print(f"{x} {inst.disas_str}")


def main():
    prepare()
    file_path = get_file_path()
    if not file_path:
        usage()
        exit()
    hex_parser = IntelHexParser(file_path)
    hex_parser.parse()

    analyze(hex_parser.records)


if __name__ == "__main__":
    main()

動作イメージ

こんな感じです

$ python3 disas.py demo2.hex | head -10
0000: effe GOTO k: FFFC Go to address  11101111kkkkkkkk 1111kkkkkkkkkkkk
2E00: 0000 NOP  No Operation 0000000000000000
2E02: 0e03 MOVLW k: 3 Move literal to WREG 00001110kkkkkkkk
2E04: 6ec6 MOVWF f: C6,a: 0 WREG を f に移動 0110111affffffff
2E06: 0e01 MOVLW k: 1 Move literal to WREG 00001110kkkkkkkk
2E08: 0104 MOVLB k: 4 Move literal to BSR<5:0> 0000000100kkkkkk
2E0A: 6f00 MOVWF f: 0,a: 1 WREG を f に移動 0110111affffffff
2E0C: 0103 MOVLB k: 3 Move literal to BSR<5:0> 0000000100kkkkkk
2E0E: 6bf4 CLRF f: F4,a: 1 f をクリア 0110101affffffff
2E10: 6bf5 CLRF f: F5,a: 1 f をクリア 0110101affffffff
0
0
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
0
0

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?