130 lines
3.5 KiB
Python
130 lines
3.5 KiB
Python
from __future__ import annotations
|
|
|
|
from typing import TypedDict
|
|
|
|
from .formatting import h16
|
|
from .memory import region_for
|
|
from .rom import Rom
|
|
from .tables import IO_REGISTERS
|
|
|
|
|
|
DTC_REGISTER_INFO_SIZE = 8
|
|
DTC_RESERVED_MODE_MASK = 0x1FFF
|
|
|
|
|
|
class DtcEndpointInfo(TypedDict):
|
|
address: int
|
|
text: str
|
|
name: str | None
|
|
region: str
|
|
increment: bool
|
|
increment_step: int
|
|
|
|
|
|
class DtcModeInfo(TypedDict):
|
|
raw: int
|
|
size: str
|
|
bytes_per_transfer: int
|
|
source_increment: bool
|
|
destination_increment: bool
|
|
source_increment_step: int
|
|
destination_increment_step: int
|
|
reserved: int
|
|
reserved_set: bool
|
|
|
|
|
|
class DtcCountInfo(TypedDict):
|
|
raw: int
|
|
transfers: int
|
|
bytes: int
|
|
zero_means_65536: bool
|
|
|
|
|
|
class DtcRegisterInfo(TypedDict, total=False):
|
|
address: int
|
|
valid: bool
|
|
error: str
|
|
dtmr: int
|
|
dtsr: int
|
|
dtdr: int
|
|
dtcr: int
|
|
mode: DtcModeInfo
|
|
source: DtcEndpointInfo
|
|
destination: DtcEndpointInfo
|
|
count: DtcCountInfo
|
|
|
|
|
|
def _endpoint(address: int, increment: bool, increment_step: int) -> DtcEndpointInfo:
|
|
name = IO_REGISTERS.get(address)
|
|
return {
|
|
"address": address,
|
|
"text": name or h16(address),
|
|
"name": name,
|
|
"region": region_for(address).name,
|
|
"increment": increment,
|
|
"increment_step": increment_step if increment else 0,
|
|
}
|
|
|
|
|
|
def _mode(dtmr: int) -> DtcModeInfo:
|
|
size = "word" if dtmr & 0x8000 else "byte"
|
|
bytes_per_transfer = 2 if size == "word" else 1
|
|
source_increment = bool(dtmr & 0x4000)
|
|
destination_increment = bool(dtmr & 0x2000)
|
|
source_step = bytes_per_transfer if source_increment else 0
|
|
destination_step = bytes_per_transfer if destination_increment else 0
|
|
reserved = dtmr & DTC_RESERVED_MODE_MASK
|
|
return {
|
|
"raw": dtmr,
|
|
"size": size,
|
|
"bytes_per_transfer": bytes_per_transfer,
|
|
"source_increment": source_increment,
|
|
"destination_increment": destination_increment,
|
|
"source_increment_step": source_step,
|
|
"destination_increment_step": destination_step,
|
|
"reserved": reserved,
|
|
"reserved_set": reserved != 0,
|
|
}
|
|
|
|
|
|
def decode_dtc_register_info(rom: Rom, address: int) -> DtcRegisterInfo:
|
|
"""Decode the four-word DTMR/DTSR/DTDR/DTCR block pointed to by a DTC vector."""
|
|
end = address + DTC_REGISTER_INFO_SIZE - 1
|
|
if end > 0xFFFF:
|
|
return {
|
|
"address": address,
|
|
"valid": False,
|
|
"error": f"register information block {h16(address)}+{DTC_REGISTER_INFO_SIZE} exceeds page 0",
|
|
}
|
|
if not rom.contains(address, DTC_REGISTER_INFO_SIZE):
|
|
return {
|
|
"address": address,
|
|
"valid": False,
|
|
"error": f"register information block {h16(address)}-{h16(end)} is outside ROM image",
|
|
}
|
|
|
|
dtmr = rom.u16(address)
|
|
dtsr = rom.u16(address + 2)
|
|
dtdr = rom.u16(address + 4)
|
|
dtcr = rom.u16(address + 6)
|
|
mode = _mode(dtmr)
|
|
transfers = 0x10000 if dtcr == 0 else dtcr
|
|
|
|
return {
|
|
"address": address,
|
|
"valid": True,
|
|
"dtmr": dtmr,
|
|
"dtsr": dtsr,
|
|
"dtdr": dtdr,
|
|
"dtcr": dtcr,
|
|
"mode": mode,
|
|
"source": _endpoint(dtsr, mode["source_increment"], mode["source_increment_step"]),
|
|
"destination": _endpoint(dtdr, mode["destination_increment"], mode["destination_increment_step"]),
|
|
"count": {
|
|
"raw": dtcr,
|
|
"transfers": transfers,
|
|
"bytes": transfers * mode["bytes_per_transfer"],
|
|
"zero_means_65536": dtcr == 0,
|
|
},
|
|
}
|