# Copyright 2024-2025 NXP

# SPDX-License-Identifier: BSD-3-Clause

import binascii
import gdb
import os
import sys
from gdb.disassembler import Disassembler
from gdb.disassembler import DisassemblerResult

CURRENT_DIR = "".join([os.path.dirname(os.path.realpath(__file__)), "/"])
sys.path.insert(0, CURRENT_DIR)

DEFAULT_ARCH = "rv32i2p0"

import disassemble_utils as utils


# gdb server has to include the 32bit dll
class RiscVDisassembler(Disassembler):

    def __init__(self):
        self.supported = None
        self.conn = None
        self.arch = None
        self.elf_path = None
        super(RiscVDisassembler, self).__init__("RiscVDisassembler")

    def __call__(self, info):
        if self.supported is None:
            self.disassembler_init()

        if not self.supported:
            result = gdb.disassembler.builtin_disassemble(info)
            length = result.length
            text = result.string
        else:
            text, length = self.get_disassemble_info(info)
        return DisassemblerResult(length, text)

    def disassembler_init(self):
        self.supported = False
        # attempt to read arch from ELF
        prog_space = gdb.current_progspace()
        self.elf_path = prog_space.filename
        self.arch = utils.parse_riscv_attributes(self.elf_path)
        if not self.arch:
            return
        self.conn = gdb.selected_inferior().connection
        if isinstance(self.conn, gdb.RemoteTargetConnection):
            resp = self.conn.send_packet("vDis?")
            if resp == b'OK' and self.arch:
                self.supported = True
                self.arch = binascii.hexlify(self.arch.encode("ascii")).decode("ascii").lower()
                self.conn.send_packet("vDis:c:n{}:".format(self.arch))

    def get_disassemble_info(self, info):
        mem = info.read_memory(4, 0)
        instr_bytes = bytes(mem)
        length, text = self.get_disassembled_instr(instr_bytes, info)
        text = utils.validate_result(text, length, instr_bytes)
        return text, length

    def get_disassembled_instr(self, instr_bytes, info):
        code = utils.int_from_bytes(instr_bytes)
        resp = self.conn.send_packet("vDis:d:{:x}:{:08x}".format(info.address, code))
        # fallback to builtin disassembler in case of error
        if resp.decode().startswith("E"):
            result = gdb.disassembler.builtin_disassemble(info)
            length = result.length
            text = result.string
            return length, text
        fields = resp.decode().split(":")
        length = int(fields[0])
        text = binascii.unhexlify(fields[1]).decode().split("\t", 1)[1]
        prop_instr = int(fields[2])
        try:
            target_address = int(fields[4], base=16)
            text = text + utils.get_target_address_value(target_address)
        except IndexError:
            pass
        return length, text
