# gdb-helpers/rp2040.py - GDB helpers for the RP2040 CPU.
#
# Copyright (C) 2024  Luke T. Shumaker <lukeshu@lukeshu.com>
# SPDX-License-Identifier: AGPL-3.0-or-later

import gdb


def read_mmreg(addr: int) -> int:
    return int(gdb.parse_and_eval(f"*(uint32_t*)({addr})"))


def read_reg(name: str) -> int:
    return int(gdb.selected_frame().read_register(name))


def fmt32(x: int) -> str:
    return "0b" + bin(x)[2:].rjust(32, "0")


def read_prio(addr: int) -> str:
    prios: list[int] = 32 * [0]
    for regnum in range(0, 8):
        regval = read_mmreg(addr + (regnum * 4))
        prios[(regnum * 4) + 0] = (regval >> (0 + 6)) & 0b11
        prios[(regnum * 4) + 1] = (regval >> (8 + 6)) & 0b11
        prios[(regnum * 4) + 2] = (regval >> (16 + 6)) & 0b11
        prios[(regnum * 4) + 3] = (regval >> (24 + 6)) & 0b11
    return "  " + "".join(str(p) for p in reversed(prios))


nvic_names = [
    "TIMER_0",  # 0
    "TIMER_1",  # 1
    "TIMER_2",  # 2
    "TIMER_3",  # 3
    "PWM",  # 4
    "USB",  # 5
    "XIP",  # 6
    "PIO0_0",  # 7
    "PIO0_1",  # 8
    "PIO1_0",  # 9
    "PIO1_1",  # 10
    "DMA_0",  # 11
    "DMA_1",  # 12
    "IO_BANK0",  # 13
    "IO_QSPI",  # 14
    "SIO_PROC0",  # 15
    "SIO_PROC1",  # 16
    "CLOCKS",  # 17
    "SPI0",  # 18
    "SPI1",  # 19
    "UART0",  # 20
    "UART1",  # 21
    "ADC_FIFO",  # 22
    "I2C0",  # 23
    "I2C1",  # 24
    "RTC",  # 25
    "unused(26)",  # 26
    "unused(27)",  # 27
    "unused(28)",  # 28
    "unused(29)",  # 29
    "unused(30)",  # 30
    "unused(31)",  # 31
]

exception_names = [
    "none",  # 0
    "reset",  # 1
    "NMI",  # 2
    "hard fault",  # 3
    "reserved(4)",  # 4
    "reserved(5)",  # 5
    "reserved(6)",  # 6
    "reserved(7)",  # 7
    "reserved(8)",  # 8
    "reserved(9)",  # 9
    "reserved(10)",  # 10
    "SVCall",  # 11
    "reserved(12)",  # 12
    "reserved(13)",  # 13
    "PendSV",  # 14
    "SysTick",  # 15
    *[f"nvic_{nvic}" for nvic in nvic_names],
]
while len(exception_names) < 1 << 9:
    exception_names += [f"unused({len(exception_names)})"]


class RP2040ShowInterrupts(gdb.Command):
    """Show the RP2040's interrupt state."""

    def __init__(self) -> None:
        super(RP2040ShowInterrupts, self).__init__(
            "rp2040-show-interrupts", gdb.COMMAND_USER
        )

    def invoke(self, arg: str, from_tty: bool) -> None:
        base: int = 0xE0000000
        icsr = read_mmreg(base + 0xED04)
        psr = read_reg("xPSR")
        #       ║├┤║├┤├┤║├┤║║├┤├──┤║║║├──┤
        # 10987654321098765432109876543210 (dec bitnum)
        #  3         2         1         0
        #       ║├┤║├┤├┤║├┤║║├┤├──┤║║║├──┤
        # fedcba9876543210fedcba9876543210 (hex bitnum)
        #                1               0
        print(
            f"""
ARM Cortex-M0+ memory-mapped registers:

                     clocks╖ ┌SIO
                      SPI┐ ║ │╓QSPI
                    UART┐│ ║ │║╓bank0 ╓XIP
                   ADC╖ ││ ║ │║║┌DMA  ║╓USB
                 I2C┐ ║ ││ ║ │║║│ ┌PIO║║╓PWM
                RTC╖├┐║┌┤├┐║┌┤║║├┐├──┐║║║┌──┬timers
NVIC_ISER: {fmt32(read_mmreg(base+0xe100))  } Set-Enable
NVIC_ICER: {fmt32(read_mmreg(base+0xe180))  } Clear-Enable
NVIC_ISPR: {fmt32(read_mmreg(base+0xe200))  } Set-Pending
NVIC_ICPR: {fmt32(read_mmreg(base+0xe280))  } Clear-Pending
                   ║├┤║├┤├┤║├┤║║├┤├──┤║║║├──┤
NVIC_IPR-: {read_prio(base+0xe400)          } Priority (0=highest, 3=lowest)

           isr_pending╖
          isr_preempt╖║
        pend_st_clr╖ ║║
       pend_st_set╖║ ║║
      pend_sv_clr╖║║ ║║      ┌vect_pending
     pend_sv_set╖║║║ ║║      |           ┌vect_active
 nmi_pend_set╖  ║║║║ ║║ ┌────┴──┐   ┌────┴──┐
ICSR     : {fmt32(icsr)                     } Control and State
                        └────┬──┘   └────┬──┘
                             |           └{icsr&0x1FF} ({exception_names[icsr&0x1FF]})
                             └{(icsr>>11)&0x1FF} ({exception_names[(icsr>>11)&0x1FF]})

                             ╓endiannes (0=little, 1=big)
             vect_key┐       ║            ╓sys_reset_req
             ┌───────┴──────┐║            ║╓vect_clr_active
AIRCR    : {fmt32(read_mmreg(base+0xed0c))  } Application Interrupt and Reset Control

                                          ╓sleep_deep
                            s_ev_on_pend╖ ║╓sleep_on_exit
SCR      : {fmt32(read_mmreg(base+0xed10))  } System Control

ARM Cortex-M0+ processor core registers:

                                            ╓pm (0=normal, 1=top priority)
PRIMASK  : {fmt32(read_reg('primask'))      } Priority Mask

        [C]arry╖╓o[V]erflow
        [Z]ero╖║║   ╓[T]humb       ╓require [a]lignment
   [N]egative╖║║║   ║      exec    ║     ┌interrupt
          app╫╫╫╢   ╟───────┴──────╢┌────┴──┐
xPSR     : {fmt32(psr)                      } {{Application,Execution,Interrupt}} Program Status
                                    └────┬──┘
                                         └{psr&0x1FF} ({exception_names[psr&0x1FF]})

"""
        )


RP2040ShowInterrupts()