Wii-U-Firmware-Emulator/hardware.py
2019-05-02 20:35:40 +02:00

2721 lines
84 KiB
Python

from Crypto.Cipher import AES
import struct
import sys
import pyemu
if "noprint" in sys.argv:
def print(*args): pass
AHMN_D8B0010 = 0xD0B0010
AHMN_MEM0_CONFIG = 0xD0B0800
AHMN_MEM1_CONFIG = 0xD0B0804
AHMN_MEM2_CONFIG = 0xD0B0808
AHMN_RDBI_MASK = 0xD0B080C
AHMN_ERROR_MASK = 0xD0B0820
AHMN_ERROR = 0xD0B0824
AHMN_D8B0840 = 0xD0B0840
AHMN_D8B0844 = 0xD0B0844
AHMN_TRANSFER_STATE = 0xD0B0850
AHMN_WORKAROUND = 0xD0B0854
AHMN_MEM0_START = 0xD0B0900
AHMN_MEM1_START = 0xD0B0A00
AHMN_MEM2_START = 0xD0B0C00
AHMN_MEM0_END = 0xD0B0980
AHMN_MEM1_END = 0xD0B0C00
AHMN_MEM2_END = 0xD0B1000
AHMN_START = 0xD0B0000
AHMN_END = 0xD0B1000
class AHMNController:
def __init__(self, scheduler):
self.mem0_config = 0
self.mem1_config = 0
self.mem2_config = 0
self.workaround = 0
self.mem0 = [0] * ((AHMN_MEM0_END - AHMN_MEM0_START) // 4)
self.mem1 = [0] * ((AHMN_MEM1_END - AHMN_MEM1_START) // 4)
self.mem2 = [0] * ((AHMN_MEM2_END - AHMN_MEM2_START) // 4)
self.scheduler = scheduler
def read(self, addr):
if addr == AHMN_MEM0_CONFIG: return self.mem0_config
elif addr == AHMN_MEM1_CONFIG: return self.mem1_config
elif addr == AHMN_MEM2_CONFIG: return self.mem2_config
elif addr == AHMN_TRANSFER_STATE: return 0
elif addr == AHMN_WORKAROUND: return self.workaround
elif AHMN_MEM0_START <= addr < AHMN_MEM0_END: return self.mem0[(addr - AHMN_MEM0_START) // 4]
elif AHMN_MEM1_START <= addr < AHMN_MEM1_END: return self.mem1[(addr - AHMN_MEM1_START) // 4]
elif AHMN_MEM2_START <= addr < AHMN_MEM2_END: return self.mem2[(addr - AHMN_MEM2_START) // 4]
print("AHMN READ 0x%X at %08X" %(addr, self.scheduler.pc()))
value = 0
def write(self, addr, value):
if addr == AHMN_D8B0010: pass
elif addr == AHMN_MEM0_CONFIG: self.mem0_config = value
elif addr == AHMN_MEM1_CONFIG: self.mem1_config = value
elif addr == AHMN_MEM2_CONFIG: self.mem2_config = value
elif addr == AHMN_RDBI_MASK: pass
elif addr == AHMN_ERROR_MASK: pass
elif addr == AHMN_ERROR: pass
elif addr == AHMN_D8B0840: pass
elif addr == AHMN_D8B0844: pass
elif addr == AHMN_WORKAROUND: self.workaround = value
elif AHMN_MEM0_START <= addr < AHMN_MEM0_END: self.mem0[(addr - AHMN_MEM0_START) // 4] = value ^ 0x80000000
elif AHMN_MEM1_START <= addr < AHMN_MEM1_END: self.mem1[(addr - AHMN_MEM1_START) // 4] = value ^ 0x80000000
elif AHMN_MEM2_START <= addr < AHMN_MEM2_END: self.mem2[(addr - AHMN_MEM2_START) // 4] = value ^ 0x80000000
else:
print("AHMN WRITE 0x%X %08X at %08X" %(addr, value, self.scheduler.pc()))
MEM_D8B4200 = 0xD0B4200
MEM_FLUSH_MASK = 0xD0B4228
MEM_EDRAM_REFRESH_CTRL = 0xD0B42CC
MEM_EDRAM_REFRESH_VAL = 0xD0B42CE
MEM_BLOCK_MEM0_CFG = 0xD0B4400
MEM_BLOCK_MEM1_CFG = 0xD0B4402
MEM_BLOCK_MEM2_CFG = 0xD0B4404
MEM_UNK_START = 0xD0B440E
MEM_UNK_END = 0xD0B44C6
MEM_D8B44E6 = 0xD0B44E6
MEM_D8B44E8 = 0xD0B44E8
MEM_D8B44EA = 0xD0B44EA
MEM_START = 0xD0B4000
MEM_END = 0xD0B4800
class MEMController:
def __init__(self, scheduler):
self.d8b4200 = 0
self.mem0_config = 0
self.mem1_config = 0
self.mem2_config = 0
self.scheduler = scheduler
def read(self, addr):
if addr == MEM_D8B4200: return self.d8b4200
elif addr == MEM_FLUSH_MASK: return 0
elif addr == MEM_BLOCK_MEM0_CFG: return self.mem0_config
elif addr == MEM_BLOCK_MEM1_CFG: return self.mem1_config
elif addr == MEM_BLOCK_MEM2_CFG: return self.mem2_config
print("MEM READ 0x%X at %08X" %(addr, self.scheduler.pc()))
return 0
def write(self, addr, value):
if addr == MEM_D8B4200: self.d8b4200 = value
elif addr == MEM_FLUSH_MASK: pass
elif addr == MEM_EDRAM_REFRESH_CTRL: pass
elif addr == MEM_EDRAM_REFRESH_VAL: pass
elif addr == MEM_BLOCK_MEM0_CFG: self.mem0_config = value
elif addr == MEM_BLOCK_MEM1_CFG: self.mem1_config = value
elif addr == MEM_BLOCK_MEM2_CFG: self.mem2_config = value
elif MEM_UNK_START <= addr < MEM_UNK_END: pass
elif addr == MEM_D8B44E6: pass
elif addr == MEM_D8B44E8: pass
elif addr == MEM_D8B44EA: pass
else:
print("MEM WRITE 0x%X %08X at %08X" %(addr, value, self.scheduler.pc()))
DI2SATA_CVR = 0xD006004
DI2SATA_START = 0xD006000
DI2SATA_END = 0xD00602C
class DI2SATAController:
def __init__(self, scheduler):
self.scheduler = scheduler
def read(self, addr):
if addr == DI2SATA_CVR: return 1 #No disc?
print("DI2SATA READ 0x%X at %08X" %(addr, self.scheduler.pc()))
return 0
def write(self, addr, value):
print("DI2SATA WRITE 0x%X %08X at %08X" %(addr, value, self.scheduler.pc()))
class RTCController:
def __init__(self, scheduler):
self.sram = [0] * 0x10
self.data = 0
self.temp_data = 0
self.write_state = 0
self.write_offset = 0
self.on_timer = 0
self.off_timer = 0
self.control_reg0 = 0
self.control_reg1 = 0
self.scheduler = scheduler
def update_data(self):
self.data = self.temp_data
def handle(self, value):
if self.write_state:
self.write_state -= 1
self.handle_write(value)
elif value & 0x80000000:
self.write_state = 0x10 if value == 0xA0000100 else 1
self.write_offset = value & ~0x80000000
else:
self.temp_data = self.handle_read(value)
def handle_read(self, offset):
if offset == 0x21000000: return self.on_timer
elif offset == 0x21000100: return self.off_timer
elif offset == 0x21000C00: return self.control_reg0
elif offset == 0x21000D00: return self.control_reg1
else:
print("RTC READ %08X at %08X" %(offset, self.scheduler.pc()))
return 0
def handle_write(self, value):
if self.write_offset == 0x20000100:
self.sram[-self.write_state] = value
elif self.write_offset == 0x21000000:
self.on_timer = value & 0x3FFFFFFF
elif self.write_offset == 0x21000100:
self.off_timer = value & 0x3FFFFFFF
elif self.write_offset == 0x21000D00:
if value & 0x10000:
raise NotImplementedError("RTC power off")
if value & 0x100:
raise NotImplementedError("RTC sleep mode")
self.control_reg1 = value
else:
print("RTC WRITE %08X %08X at %08X" %(self.write_offset, value, self.scheduler.pc()))
EXI0_CSR = 0xD006800
EXI0_CR = 0xD00680C
EXI0_DATA = 0xD006810
EXI_START = 0xD006800
EXI_END = 0xD00683C
class EXIController:
def __init__(self, scheduler, armirq):
self.rtc = RTCController(scheduler)
self.scheduler = scheduler
self.armirq = armirq
self.csr0 = 0
self.data0 = 0
def read(self, addr):
if addr == EXI0_CSR: return self.csr0
elif addr == EXI0_DATA: return self.rtc.data
print("EXI READ 0x%X at %08X" %(addr, self.scheduler.pc()))
return 0
def write(self, addr, value):
if addr == EXI0_CSR: self.csr0 = value
elif addr == EXI0_CR:
if value == 0x31: #Update in data
self.rtc.update_data()
self.armirq.trigger_irq_all(20)
elif value == 0x35: #Send data
self.rtc.handle(self.data0)
self.armirq.trigger_irq_all(20)
self.csr0 |= 4
else:
print("EXI0_CR WRITE %08X at %08X" %(value, self.scheduler.pc()))
elif addr == EXI0_DATA:
self.data0 = value
else:
print("EXI WRITE 0x%X %08X at %08X" %(addr, value, self.scheduler.pc()))
EHCI_CMD = 0
EHCI_STATUS = 4
EHCI_INTR = 8
EHCI_FRAME_INDEX = 0xC
EHCI_CTRL_SEGMENT = 0x10
EHCI_PER_LIST_BASE = 0x14
EHCI_ASYNC_LIST_ADDR = 0x18
EHCI_CONFIG_FLAG = 0x40
EHCI0_START = 0xD040000
EHCI0_END = 0xD050000
EHCI1_START = 0xD120000
EHCI1_END = 0xD130000
EHCI2_START = 0xD140000
EHCI2_END = 0xD150000
class EHCIController:
def __init__(self, scheduler, index):
self.reset()
self.scheduler = scheduler
self.index = index
def reset(self):
self.cmd = 0
self.status = 0x1000
self.intr = 0
self.frame_index = 0
self.control_segment = 0
self.per_list_addr = 0
self.async_list_addr = 0
self.config_flag = 0
def read(self, addr):
if addr == EHCI_CMD: return self.cmd
elif addr == EHCI_STATUS: return self.status
elif addr == EHCI_INTR: return self.intr
elif addr == EHCI_FRAME_INDEX: return self.frame_index
elif addr == EHCI_CTRL_SEGMENT: return self.control_segment
print("EHCI(%i) READ 0x%X at %08X" %(self.index, addr, self.scheduler.pc()))
return 0
def write(self, addr, value):
if addr == EHCI_CMD:
if value & 2:
self.reset()
value &= ~2
if value & 1:
self.execute()
value &= ~1
self.cmd = value
elif addr == EHCI_STATUS: self.status &= ~value
elif addr == EHCI_INTR: self.intr = value
elif addr == EHCI_FRAME_INDEX: self.frame_index = value
elif addr == EHCI_PER_LIST_BASE: self.per_list_addr = value
elif addr == EHCI_ASYNC_LIST_ADDR: self.async_list_addr = value
elif addr == EHCI_CONFIG_FLAG: self.config_flag = value
else:
print("EHCI(%i) WRITE 0x%X %08X at %08X" %(self.index, addr, value, self.scheduler.pc()))
def execute(self):
print("EHCI(%i) EXECUTE at %08X" %(self.index, self.scheduler.pc()))
class USBDevice:
device_descriptor = b"\x12\x01\x03\x10\x01\x00\x00\x10\x7E\x05\x05\x03\x09\x99\x01\x02\x03\x01"
config_descriptor = b"\x09\x02\x19\x00\x01\x01\x04\x00\x32"
interface_descriptor = b"\x09\x04\x00\x00\x00\x00\x00\x00\x05"
endpoint_descriptor = b"\x07\x05\x81\x00\x00\x20\x04"
def __init__(self):
self.data = b""
self.config_value = 0
def handle(self, data):
type, request, value, index, length = \
struct.unpack("<BBHHH", data)
if request == 5:
print("USB SET_ADDRESS 0x%X" %value)
elif request == 6:
print("USB GET_DESCRIPTOR 0x%X 0x%X 0x%X" %(value, index, length))
desc_type = value >> 8
if desc_type == 1: #DEVICE
self.data = self.device_descriptor
elif desc_type == 2: #CONFIGURATION
self.data = self.config_descriptor + self.interface_descriptor + self.endpoint_descriptor
elif request == 9:
print("USB SET_CONFIGURATION 0x%X" %value)
self.config_value = value
else:
print("USB REQUEST", type, request, value, index, length)
def send(self, data):
print("USB DATA 0x%X" %len(data))
def receive(self, size):
return self.data[:size]
OHCI00_START = 0xD050000
OHCI00_END = 0xD060000
OHCI01_START = 0xD060000
OHCI01_END = 0xD070000
OHCI1_START = 0xD130000
OHCI1_END = 0xD140000
OHCI2_START = 0xD150000
OHCI2_END = 0xD160000
OHCI_REVISION = 0
OHCI_CONTROL = 4
OHCI_CMD_STATUS = 8
OHCI_INT_STATUS = 0xC
OHCI_INT_ENABLE = 0x10
OHCI_INT_DISABLE = 0x14
OHCI_HCCA = 0x18
OHCI_PERIOD_CURRENT_ED = 0x1C
OHCI_CONTROL_HEAD_ED = 0x20
OHCI_CONTROL_CURRENT_ED = 0x24
OHCI_BULK_HEAD_ED = 0x28
OHCI_BULK_CURRENT_ED = 0x2C
OHCI_DONE_HEAD = 0x30
OHCI_FM_INTERVAL = 0x34
OHCI_FM_REMAINING = 0x38
OHCI_FM_NUMBER = 0x3C
OHCI_PERIODIC_START = 0x40
OHCI_LS_THRESHOLD = 0x44
OHCI_RH_DESCRIPTOR_A = 0x48
OHCI_RH_DESCRIPTOR_B = 0x4C
OHCI_RH_STATUS = 0x50
OHCI_RH_PORT_STATUS = 0x54
class OHCIController:
num_ports = 1
def __init__(self, scheduler, armirq, physmem, index):
self.scheduler = scheduler
self.armirq = armirq
self.physmem = physmem
self.index = index
self.reset()
self.device = USBDevice()
def reset(self):
self.control = 0
self.int_status = 0
self.int_enable = 0
self.hcca = 0
self.done_head = 0
self.frame_interval = 0
self.periodic_start = 0
self.descriptor_a = (1 << 24) | self.num_ports
self.descriptor_b = 0
self.control_head_ed = 0
self.bulk_head_ed = 0
self.port_enable = [0] * self.num_ports
self.port_suspend = [0] * self.num_ports
self.port_power = [0] * self.num_ports
self.port_reset_change = [0] * self.num_ports
self.port_enable_change = [0] * self.num_ports
self.port_suspend_change = [0] * self.num_ports
self.port_reset_change = [0] * self.num_ports
def read(self, addr):
if addr == OHCI_REVISION: return 0x10
elif addr == OHCI_CONTROL: return self.control
elif addr == OHCI_CMD_STATUS: return 0
elif addr == OHCI_INT_STATUS: return self.int_status
elif addr == OHCI_INT_ENABLE: return self.int_enable
elif addr == OHCI_CONTROL_HEAD_ED: return self.control_head_ed
elif addr == OHCI_RH_DESCRIPTOR_A: return self.descriptor_a
elif addr == OHCI_RH_DESCRIPTOR_B: return self.descriptor_b
elif addr == OHCI_RH_STATUS: return 0
elif OHCI_RH_PORT_STATUS <= addr < OHCI_RH_PORT_STATUS + self.num_ports * 4:
i = (addr - OHCI_RH_PORT_STATUS) // 4
return 1 | (self.port_enable[i] << 1) | (self.port_suspend[i] << 2) | (self.port_power[i] << 8) | \
self.port_reset_change[i] << 20
print("OHCI(%i) READ 0x%X at %08X" %(self.index, addr, self.scheduler.pc()))
return 0
def write(self, addr, value):
if addr == OHCI_CONTROL: self.control = value
elif addr == OHCI_CMD_STATUS:
if value & 1: self.reset()
if value & 2: self.process_control()
if value & 4: self.process_bulk()
elif addr == OHCI_INT_STATUS: self.int_status &= ~value
elif addr == OHCI_INT_ENABLE: self.int_enable |= value
elif addr == OHCI_INT_DISABLE: self.int_enable &= ~value
elif addr == OHCI_HCCA: self.hcca = value
elif addr == OHCI_CONTROL_HEAD_ED: self.control_head_ed = value
elif addr == OHCI_BULK_HEAD_ED: self.bulk_head_ed = value
elif addr == OHCI_FM_INTERVAL: self.frame_interval = value
elif addr == OHCI_PERIODIC_START: self.periodic_start = value
elif addr == OHCI_RH_DESCRIPTOR_A: self.descriptor_a = value
elif addr == OHCI_RH_DESCRIPTOR_B: self.descriptor_b = value
elif addr == OHCI_RH_STATUS:
if value & 1: self.port_power = [0] * self.num_ports
if value & 0x10000: self.port_power = [1] * self.num_ports
elif OHCI_RH_PORT_STATUS <= addr < OHCI_RH_PORT_STATUS + self.num_ports * 4:
i = (addr - OHCI_RH_PORT_STATUS) // 4
if value & 1: self.port_enable[i] = 0; self.port_enable_change[i] = 1
if value & 2: self.port_enable[i] = 1; self.port_enable_change[i] = 1
if value & 4: self.port_suspend[i] = 1; self.port_suspend_change[i] = 1
if value & 8: self.port_suspend[i] = 0; self.port_suspend_change[i] = 1
if value & 0x10: self.port_reset_change[i] = 1
if value & 0x100: self.port_power[i] = 1
if value & 0x200: self.port_power[i] = 0
if value & 0x20000: self.port_enable_change[i] = 0
if value & 0x40000: self.port_suspend_change[i] = 0
if value & 0x100000: self.port_reset_change[i] = 0
else:
print("OHCI(%i) WRITE 0x%X %08X at %08X" %(self.index, addr, value, self.scheduler.pc()))
def trigger_irq(self, flag):
assert self.index == 1
assert self.int_enable & flag
self.int_status |= flag
if self.index == 1:
self.armirq.trigger_irq_all(6)
def process_control(self):
assert self.control & 0x10
self.process_eds(self.control_head_ed)
def process_bulk(self):
assert self.control & 0x20
self.process_eds(self.bulk_head_ed)
def process_eds(self, current_ed):
while current_ed:
control, tail_td, current_td, next_ed = \
struct.unpack("<IIII", self.physmem.read(current_ed, 0x10))
tail_td &= ~0xF
next_ed &= ~0xF
if not control & 0x4000 and not current_td & 1: #Check skip and halt bits
self.process_tds(current_ed, current_td & ~0xF, tail_td)
current_ed = next_ed
current_td &= ~0xF
self.physmem.write(self.hcca + 0x84, struct.pack("<I", self.done_head))
self.done_head = 0
self.trigger_irq(2)
def process_tds(self, base_ed, current, tail):
while current != tail:
control, current_ptr, next, end_ptr = \
struct.unpack("<IIII", self.physmem.read(current, 0x10))
size = (end_ptr - current_ptr + 1) & 0xFFFFFFFF
direction = (control >> 19) & 3
if direction == 0:
data = self.physmem.read(current_ptr, size)
self.device.handle(data)
elif direction == 1:
data = self.physmem.read(current_ptr, size)
self.device.send(data)
elif direction == 2:
data = self.device.receive(size)
self.physmem.write(current_ptr, data)
else:
raise ValueError("OHCI(%i): DIR=RESERVED" %self.index)
#Set condition code
control = control & ~0xF0000000 #No error
self.physmem.write(current, struct.pack("<I", control))
#Remove from ed
dword = struct.unpack("<I", self.physmem.read(base_ed + 8, 4))[0]
dword = (dword & 0xF) | next
self.physmem.write(base_ed + 8, struct.pack("<I", dword))
#Add to done queue
self.physmem.write(current + 8, struct.pack("<I", self.done_head))
self.done_head = current
current = next
AHCI_HBA_CONTROL = 0xD160404
AHCI_HBA_INT_STATUS = 0xD160408
AHCI_CMD_BASE = 0xD160500
AHCI_CMD_BASE_HI = 0xD160504
AHCI_FIS_BASE = 0xD160508
AHCI_FIS_BASE_HI = 0xD16050C
AHCI_INT_STATUS = 0xD160510
AHCI_INT_ENABLE = 0xD160514
AHCI_CMD_STATUS = 0xD160518
AHCI_TASK_FILE_DATA = 0xD160520
AHCI_STATUS = 0xD160528
AHCI_CONTROL = 0xD16052C
AHCI_ERROR = 0xD160530
AHCI_CMD_ISSUE = 0xD160538
SATA_INT_STATE = 0xD160800
SATA_INT_MASK = 0xD160804
AHCI_START = 0xD160000
AHCI_END = 0xD170000
FIS_TYPE_REG_H2D = 0x27
ATAPI_REQUEST_SENSE = 3
ATAPI_CF_AUTHENTICATE = 0xF1
ATAPI_CF_START_STOP_UNIT_CMD = 0xF2
ATAPI_CF_READ_CMD = 0xF3
ATAPI_CF_INQUIRY = 0xF5
class PRDTEntry:
def __init__(self, physmem, data):
self.physmem = physmem
dwords = struct.unpack("<4I", data)
self.data_addr = dwords[0]
self.interrupt = dwords[3] >> 31
self.byte_count = (dwords[3] & 0x3FFFFF) + 1
def write(self, data):
if len(data) > self.byte_count:
raise RuntimeError("PRDT write overflow")
self.physmem.write(self.data_addr, data)
class AHCICmdList:
def __init__(self, physmem, addr):
self.physmem = physmem
self.addr = addr
data = physmem.read(addr, 0x20)
prdt_count = struct.unpack_from("<H", data, 2)[0]
table_addr = struct.unpack_from("<I", data, 8)[0]
self.fis = physmem.read(table_addr, 0x40)
self.atapi = physmem.read(table_addr + 0x40, 0x10)
prdt_data = physmem.read(table_addr + 0x80, prdt_count * 0x10)
self.prdts = [PRDTEntry(physmem, prdt_data[i * 0x10 : (i + 1) * 0x10]) for i in range(prdt_count)]
self.byte_count = 0
def add_bytes(self, num):
self.byte_count += num
self.physmem.write(self.addr + 4, struct.pack("<I", self.byte_count))
def write_prdt(self, index, data):
self.prdts[index].write(data)
self.add_bytes(len(data))
def fill_prdts(self):
for i, prdt in enumerate(self.prdts):
self.write_prdt(i, b"\0" * prdt.byte_count)
class AHCIController:
def __init__(self, scheduler, armirq, physmem):
self.scheduler = scheduler
self.armirq = armirq
self.physmem = physmem
self.hba_control = 0
self.hba_int_status = 0
self.cmd_base = 0
self.fis_base = 0
self.int_status = 0
self.int_enable = 0
self.cmd_status = 0
self.status = 0x3
self.sata_int_state = 0
self.sata_int_mask = 0
def read(self, addr):
if addr == AHCI_HBA_CONTROL: return self.hba_control
elif addr == AHCI_HBA_INT_STATUS: return self.hba_int_status
elif addr == AHCI_CMD_BASE: return self.cmd_base
elif addr == AHCI_CMD_BASE_HI: return 0
elif addr == AHCI_FIS_BASE: return self.fis_base
elif addr == AHCI_FIS_BASE_HI: return 0
elif addr == AHCI_INT_STATUS: return self.int_status
elif addr == AHCI_INT_ENABLE: return self.int_enable
elif addr == AHCI_CMD_STATUS: return self.cmd_status
elif addr == AHCI_TASK_FILE_DATA: return 0
elif addr == AHCI_STATUS: return self.status
elif addr == AHCI_ERROR: return 0
elif addr == AHCI_CMD_ISSUE: return 0
elif addr == SATA_INT_STATE: return self.sata_int_state
elif addr == SATA_INT_MASK: return self.sata_int_mask
print("AHCI READ 0x%X at %08X" %(addr, self.scheduler.pc()))
return 0
def write(self, addr, value):
if addr == AHCI_HBA_CONTROL: self.hba_control = value & ~1
elif addr == AHCI_HBA_INT_STATUS: self.hba_int_status &= ~value
elif addr == AHCI_CMD_BASE: self.cmd_base = value & ~0x3FF
elif addr == AHCI_CMD_BASE_HI: pass
elif addr == AHCI_FIS_BASE: self.fis_base = value & ~0xFF
elif addr == AHCI_FIS_BASE_HI: pass
elif addr == AHCI_INT_STATUS: self.int_status &= ~value
elif addr == AHCI_INT_ENABLE: self.int_enable = value
elif addr == AHCI_CMD_STATUS: self.cmd_status = value
elif addr == AHCI_CONTROL: pass
elif addr == AHCI_ERROR: pass
elif addr == AHCI_CMD_ISSUE:
for i in range(32):
if value & (1 << i):
self.issue_cmd(i)
elif addr == SATA_INT_STATE: self.sata_int_state = value
elif addr == SATA_INT_MASK: self.sata_int_mask = value
else:
print("AHCI WRITE 0x%X %08X at %08X" %(addr, value, self.scheduler.pc()))
def issue_cmd(self, index):
addr = self.cmd_base + index * 0x20
self.handle_cmd_table(AHCICmdList(self.physmem, addr))
self.hba_int_status = 1
self.sata_int_status = self.sata_int_mask
self.armirq.trigger_irq_all(28)
self.armirq.trigger_irq_lt(6)
def handle_cmd_table(self, cmd_list):
if cmd_list.fis[0] == FIS_TYPE_REG_H2D:
command = cmd_list.fis[2]
if command == 0xA0: #PACKET
self.handle_atapi(cmd_list)
else:
print("FIS COMMAND 0x%X at %08X" %(command, self.scheduler.pc()))
else:
print("AHCI COMMAND %i at %08X" %(data[0], self.scheduler.pc()))
def handle_atapi(self, cmd_list):
command = cmd_list.atapi[0]
if command == ATAPI_REQUEST_SENSE:
cmd_list.write_prdt(0, b"\xF0" + bytes(17) + b"\x02" + bytes(13))
elif command == ATAPI_CF_INQUIRY:
cmd_list.write_prdt(0, b"\0\0\0\x05" + b"\0" * 28)
else:
print("ATAPI CMD 0x%X at %08X" %(command, self.scheduler.pc()))
cmd_list.fill_prdts()
SDIO0_START = 0xD070000
SDIO0_END = 0xD080000
SDIO1_START = 0xD100000
SDIO1_END = 0xD110000
SDIO2_START = 0xD110000
SDIO2_END = 0xD120000
WIFI_START = 0xD080000
WIFI_END = 0xD090000
class SDIOController:
TYPE_SD = 0
TYPE_SDIO = 1
TYPE_MMC = 2
TYPE_UNK = 3
STATE_IDLE = 0
STATE_READY = 1
STATE_IDENT = 2
STATE_STBY = 3
STATE_TRAN = 4
STATE_DATA = 5
STATE_RCV = 6
STATE_PRG = 7
STATE_DIS = 8
capabilities = 0
voltage_range = 0x300000
cis_pointer = 0x1000
card_info = b"\x22\x04\x00\xFF\xFF\x32" #CISTPL_FUNCE
card_info += b"\xFF\x00" #CISTPL_END
# V1:
# [0x00XXXX32, 0x5B5XPQXX, 0xXXXXXXXX, 0xRXS0XTXU]
# V2:
# [0x400E0032, 0x5B59V0QX, 0xXXXX7F00, 0x0A20X0XU]
# P: 1XXX
# Q: 00XX
# R: X00X
# S: XXX0
# T: XX00
# U: XXX1
# V: 000X
csd = [0x00000032, 0x5B508000, 0x00000000, 0x00000001] #V1
csd = [0x400E0032, 0x5B590000, 0xFFFF7F80, 0x0A400001] #V2
def __init__(self, scheduler, armirq, physmem, index, type):
self.file = None
if index == 1:
self.file = open("D:/WiiU/MLC/mlc_work.bin", "r+b")
#self.file = open("D:/WiiU/MLC/mlc.bin", "rb")
#self.file.write = lambda *args: None
self.scheduler = scheduler
self.armirq = armirq
self.physmem = physmem
self.index = index
self.type = type
self.dma_addr = 0
self.block_count = 0
self.block_size = 0
self.argument = 0
self.transfer_mode = 0
self.command = 0
self.result0 = 0
self.result1 = 0
self.result2 = 0
self.result3 = 0
self.host_control = 0
self.power_control = 0
self.block_gap_control = 0
self.wakeup_control = 0
self.clock_control = 0
self.timeout_control = 0
self.int_status = 0
self.error_status = 0
self.block_length = 0
self.bus_width = 0
self.cd_disable = 0
self.app_cmd = False
self.state = self.STATE_IDLE
def close(self):
if self.file:
self.file.close()
def trigger_interrupt(self):
if self.index == 0:
self.armirq.trigger_irq_all(7)
else:
self.armirq.trigger_irq_lt(0)
def read(self, addr):
if addr == 0xC:
return self.transfer_mode | (self.command << 16)
elif addr == 0x10: return self.result0
elif addr == 0x14: return self.result1
elif addr == 0x18: return self.result2
elif addr == 0x1C: return self.result3
elif addr == 0x24: return 0x80000 #Write enabled
elif addr == 0x28:
return self.host_control | (self.power_control << 8) | \
(self.block_gap_control << 16) | (self.wakeup_control << 24)
elif addr == 0x2C:
return self.clock_control | (self.timeout_control << 16)
elif addr == 0x30: return self.int_status | (self.error_status << 16)
elif addr == 0x40: return self.capabilities
print("SDIO(%i) READ 0x%X at %08X" %(self.index, addr, self.scheduler.pc()))
return 0
def write(self, addr, value):
if addr == 0: self.dma_addr = value
elif addr == 4:
self.block_count = value >> 16
self.block_size = value & 0xFFF
elif addr == 8: self.argument = value
elif addr == 0xC:
self.command = value >> 16
self.transfer_mode = value & 0xFFFF
self.int_status = 3
self.error_status = 0
if self.app_cmd:
self.handle_app_command(value >> 24, self.argument)
else:
self.handle_command(value >> 24, self.argument)
self.trigger_interrupt()
elif addr == 0x28:
self.host_control = value & 0xFF
self.power_control = (value >> 8) & 0xFF
self.block_gap_control = (value >> 16) & 0xFF
self.wakeup_control = value >> 24
elif addr == 0x2C:
self.clock_control = value & 0xFFFF
if self.clock_control & 1:
self.clock_control |= 2
self.timeout_control = (value >> 16) & 0xFF
elif addr == 0x30:
self.int_status &= ~(value & 0xFF)
self.error_status &= ~(value >> 16)
else:
print("SDIO(%i) WRITE 0x%X %08X at %08X" %(self.index, addr, value, self.scheduler.pc()))
def get_card_status(self):
#Ready for data
return (self.state << 9) | 0x100 | (self.app_cmd << 5)
def handle_app_command(self, cmd, arg):
if cmd == 6: #Set bus width
self.bus_width = arg & 3
self.result0 = self.get_card_status()
elif cmd == 41: #Voltage range
if self.type == self.TYPE_SD:
self.result0 = self.voltage_range
if arg & self.voltage_range:
self.result0 |= 0xC0000000 #Ready to operate | SDHC/SDXC
else:
self.int_status |= 0x8000
else:
print("SDIO(%i) ACMD %i %08X at %08X" %(self.index, cmd, arg, self.scheduler.pc()))
self.app_cmd = False
def handle_command(self, cmd, arg):
self.result0 = 0
self.result1 = 0
self.result2 = 0
self.result3 = 0
if cmd == 0: pass #Reset
elif cmd == 1: #Voltage range
if self.type == self.TYPE_MMC:
self.result0 = self.voltage_range
if arg & self.voltage_range:
self.result0 |= 0x80000000 #Ready to operate
else:
self.int_status |= 0x8000
elif cmd == 2: #CID register
pass
elif cmd == 3:
self.result0 = 0x400 #Relative card address
elif cmd == 5: #Voltage range
if self.type == self.TYPE_SDIO:
self.result0 = self.voltage_range | 0x8000000 #Memory present
if arg & self.voltage_range:
self.result0 |= 0x80000000 #Ready to operate
else:
self.int_status |= 0x8000
elif cmd == 7: #Card select
self.state = self.STATE_STBY
self.result0 = self.get_card_status()
elif cmd == 8: #Voltage and check pattern
self.result0 = arg & 0xFFF
elif cmd == 9: #CSD register
self.result3 = ((self.csd[0] >> 8) | (self.csd[3] << 24)) & 0xFFFFFFFF
self.result2 = ((self.csd[1] >> 8) | (self.csd[0] << 24)) & 0xFFFFFFFF
self.result1 = ((self.csd[2] >> 8) | (self.csd[1] << 24)) & 0xFFFFFFFF
self.result0 = ((self.csd[3] >> 8) | (self.csd[2] << 24)) & 0xFFFFFFFF
elif cmd == 13: #Send card status
self.state = self.STATE_TRAN
self.result0 = self.get_card_status()
elif cmd == 16: #Set block len
self.block_length = arg
self.result0 = self.get_card_status()
elif cmd == 17: #Read single block
assert self.block_count == 1
self.file.seek(self.argument << 9)
self.physmem.write(self.dma_addr, self.file.read(self.block_count * self.block_size))
elif cmd == 18: #Read multiple block
self.file.seek(self.argument << 9)
self.physmem.write(self.dma_addr, self.file.read(self.block_count * self.block_size))
elif cmd == 25: #Write multiple block
data = self.physmem.read(self.dma_addr, self.block_count * self.block_size)
self.file.seek(self.argument << 9)
self.file.write(data)
elif cmd == 52: #Read/write direct
function = (arg >> 28) & 7
if function != 0:
raise NotImplementedError("SDIO(%i) CMD52 function %i at %08X" %(self.index, function, self.scheduler.pc()))
reg_addr = (arg >> 9) & 0x1FFFF
if arg & 0x80000000: #Write
self.result0 = arg & 0xFF
self.write_register(reg_addr, self.result0)
if arg & 0x8000000:
self.result0 = self.read_register(reg_addr)
else:
self.result0 = self.read_register(reg_addr)
elif cmd == 55: #App cmd
self.result0 = 0x20
self.app_cmd = True
else:
print("SDIO(%i) CMD %i %08X at %08X" %(self.index, cmd, arg, self.scheduler.pc()))
def read_register(self, addr):
if addr == 7: return self.bus_width | (self.cd_disable << 7)
elif addr == 9: return self.cis_pointer & 0xFF
elif addr == 0xA: return (self.cis_pointer >> 8) & 0xFF
elif addr == 0xB: return self.cis_pointer >> 16
elif addr == 0x13: return 1
elif addr >= 0x1000: return self.card_info[addr - 0x1000]
print("SDIO(%i) READ REG 0x%X at %08X" %(self.index, addr, self.scheduler.pc()))
return 0
def write_register(self, addr, value):
if addr == 6: pass #Reset, i/o abort
elif addr == 7:
self.bus_width = value & 3
self.cd_disable = value >> 7
else:
print("SDIO(%i) WRITE REG 0x%X 0x%02X at %08X" %(self.index, addr, value, self.scheduler.pc()))
NAND_CTRL = 0
NAND_CONFIG = 4
NAND_ADDR1 = 8
NAND_ADDR2 = 0xC
NAND_DATABUF = 0x10
NAND_ECCBUF = 0x14
class NANDBank:
#0xECDC: Samsung K9XXG08XXB
#0xADDC: Hynix HY27UF084G2B
#0x98DC: Toshiba TC58NVG2S3ETA00
#0x01DC: Spansion Undetermined
chip_id = 0xECDC
def __init__(self, scheduler, armirq, physmem, slc, slcspare, slccmpt, slccmptspare):
self.scheduler = scheduler
self.armirq = armirq
self.physmem = physmem
self.slc = slc
self.slcspare = slcspare
self.slccmpt = slccmpt
self.slccmptspare = slccmptspare
self.file = self.slccmpt
self.filespare = self.slccmptspare
self.reset()
def reset(self):
self.control = 0
self.config = 0
self.addr1 = 0
self.addr2 = 0
self.databuf = 0
self.eccbuf = 0
def set_bank(self, cmpt):
if cmpt:
self.file = self.slccmpt
self.filespare = self.slccmptspare
else:
self.file = self.slc
self.filespare = self.slcspare
def read(self, addr):
if addr == NAND_CTRL: return self.control
elif addr == NAND_CONFIG: return self.config
elif addr == NAND_ADDR1: return self.addr1
elif addr == NAND_ADDR2: return self.addr2
elif addr == NAND_DATABUF: return self.databuf
elif addr == NAND_ECCBUF: return self.eccbuf
def write(self, addr, value):
if addr == NAND_CTRL: self.control = self.start_command(value)
elif addr == NAND_CONFIG:
self.config = value
elif addr == NAND_ADDR1: self.addr1 = value
elif addr == NAND_ADDR2: self.addr2 = value
elif addr == NAND_DATABUF: self.databuf = value
elif addr == NAND_ECCBUF: self.eccbuf = value
def start_command(self, value):
if value & 0x80000000:
command = (value >> 16) & 0xFF
write = (value >> 14) & 1
read = (value >> 13) & 1
length = value & 0xFFF
self.handle_command(command, write, read, length)
if value & 0x40000000:
self.armirq.trigger_irq_all(1)
return value & ~0x80000000
else:
self.reset()
return 0
def handle_command(self, command, write, read, length):
if command == 0: #Init read
assert not read and not write and not length
elif command == 0x10: #Finish write
assert not read and not write and not length
elif command == 0x30: #Read
assert read and not write and (length == 0x840 or length == 0x40)
if length == 0x840:
self.file.seek((self.addr2 << 11) | self.addr1)
self.physmem.write(self.databuf, self.file.read(0x800))
self.filespare.seek(self.addr2 << 6)
sparedata = self.filespare.read(0x40)
self.physmem.write(self.eccbuf, sparedata)
self.physmem.write(self.eccbuf ^ 0x40, sparedata[0x30:])
else:
self.filespare.seek(self.addr2 << 6)
self.physmem.write(self.databuf, self.filespare.read(0x40))
elif command == 0x60: #Erase init 1
assert not read and not write and not length
elif command == 0x70: #Erase
assert read and not write and length == 0x40
self.physmem.write(self.databuf, b"\0" * length)
elif command == 0x80: #Write
assert not read and write and length == 0x800
data = self.physmem.read(self.databuf, length)
self.file.seek((self.addr2 << 11) | self.addr1)
self.file.write(data)
self.next_spare = self.addr2 << 6
elif command == 0x85: #Write spare
assert not read and write and length == 0x40
data = self.physmem.read(self.databuf, length)
self.filespare.seek(self.next_spare)
self.filespare.write(data)
elif command == 0x90: #Get chip id
assert read and not write and length == 0x20
self.physmem.write(self.databuf, struct.pack(">H", self.chip_id))
elif command == 0xD0: #Erase init 2
assert not read and not write and not length
elif command == 0xFF: #Reset
assert not read and not write and not length
else:
print("NAND COMMAND 0x%X %i %i 0x%X at %08X" %(command, write, read, length, self.scheduler.pc()))
NAND_BANK = 0xD010018
NAND_BANK_CTRL = 0xD010030
NAND_INT_MASK = 0xD010034
NAND_MAIN_START = 0xD010000
NAND_MAIN_END = 0xD010018
NAND_BANKS_START = 0xD010040
NAND_BANKS_END = 0xD010100
NAND_START = 0xD010000
NAND_END = 0xD020000
class NANDController:
def __init__(self, scheduler, armirq, physmem):
self.scheduler = scheduler
self.slc = open("slc_work.bin", "r+b")
self.slcspare = open("slcspare_work.bin", "r+b")
self.slccmpt = open("slccmpt_work.bin", "r+b")
self.slccmptspare = open("slccmptspare_work.bin", "r+b")
self.armirq = armirq
self.physmem = physmem
self.main_bank = self.create_bank()
self.banks = [self.create_bank() for i in range(8)]
self.reset()
def create_bank(self):
return NANDBank(self.scheduler, self.armirq, self.physmem, self.slc, self.slcspare, self.slccmpt, self.slccmptspare)
def close(self):
self.slc.close()
self.slcspare.close()
self.slccmpt.close()
self.slccmptspare.close()
def reset(self):
self.main_bank.reset()
for bank in self.banks:
bank.reset()
self.bank = 0
self.bank_control = 0
self.int_mask = 0
def read(self, addr):
if NAND_MAIN_START <= addr < NAND_MAIN_END: return self.main_bank.read(addr - NAND_MAIN_START)
elif addr == NAND_BANK: return self.bank
elif addr == NAND_BANK_CTRL: return self.bank_control
elif addr == NAND_INT_MASK: return self.int_mask
elif NAND_BANKS_START <= addr < NAND_BANKS_END:
return self.banks[(addr - NAND_BANKS_START) // 0x18].read((addr - NAND_BANKS_START) % 0x18)
print("NAND READ 0x%X at %08X" %(addr, self.scheduler.pc()))
return 0
def write(self, addr, value):
if NAND_MAIN_START <= addr < NAND_MAIN_END: self.main_bank.write(addr - NAND_MAIN_START, value)
elif addr == NAND_BANK:
self.main_bank.set_bank(not value & 2)
for bank in self.banks:
bank.set_bank(not value & 2)
self.bank = value
elif addr == NAND_BANK_CTRL:
if value & 0x80000000:
banks = (value >> 16) & 0xFF
assert (1 << banks) - 1 == value & 0xFF
for i in range(banks):
bank = self.banks[i]
bank.config = bank.start_command(bank.config)
self.int_mask &= ~(value & 0xFF)
self.bank_control = value & ~0x80000000
self.armirq.trigger_irq_all(1)
elif NAND_BANKS_START <= addr < NAND_BANKS_END:
self.banks[(addr - NAND_BANKS_START) // 0x18].write((addr - NAND_BANKS_END) % 0x18, value)
else:
print("NAND WRITE 0x%X %08X at %08X" %(addr, value, self.scheduler.pc()))
AES_CTRL = 0
AES_SRC = 4
AES_DEST = 8
AES_KEY = 0xC
AES_IV = 0x10
AES_START = 0xD020000
AES_END = 0xD030000
AESS_START = 0xD180000
AESS_END = 0xD190000
class AESController:
def __init__(self, scheduler, armirq, physmem, index):
self.scheduler = scheduler
self.armirq = armirq
self.physmem = physmem
self.index = index
self.reset()
def reset(self):
self.ctrl = 0
self.src = 0
self.dest = 0
self.key = b"\0" * 16
self.iv = b"\0" * 16
def read(self, addr):
if addr == AES_CTRL: return self.ctrl
elif addr == AES_SRC: return self.src
elif addr == AES_DEST: return self.dest
print("AES READ 0x%X at %08X" %(addr, self.scheduler.pc()))
return 0
def write(self, addr, value):
if addr == AES_CTRL:
if value & 0x80000000:
self.ctrl = (value & ~0x80000000) | 0xFFF
data = self.physmem.read(self.src, ((value & 0xFFF) + 1) * 16)
if value & 0x10000000:
if value & 0x1000:
raise NotImplementedError("AES block chain continue")
cipher = AES.new(self.key, AES.MODE_CBC, self.iv)
if value & 0x8000000:
data = cipher.decrypt(data)
else:
data = cipher.encrypt(data)
self.physmem.write(self.dest, data)
if value & 0x40000000:
self.trigger_interrupt()
else:
self.reset()
elif addr == AES_SRC: self.src = value
elif addr == AES_DEST: self.dest = value
elif addr == AES_KEY:
self.key = self.key[4:] + struct.pack(">I", value)
elif addr == AES_IV:
self.iv = self.iv[4:] + struct.pack(">I", value)
else:
print("AES WRITE 0x%X %08X at %08X" %(addr, value, self.scheduler.pc()))
def trigger_interrupt(self):
if self.index == 0: self.armirq.trigger_irq_all(2)
else: self.armirq.trigger_irq_lt(8)
SHA_CTRL = 0
SHA_SRC = 4
SHA_H0 = 8
SHA_H1 = 0xC
SHA_H2 = 0x10
SHA_H3 = 0x14
SHA_H4 = 0x18
SHA_START = 0xD030000
SHA_END = 0xD040000
SHAS_START = 0xD190000
SHAS_END = 0xD1A0000
class SHAController:
def __init__(self, scheduler, armirq, physmem, index):
self.scheduler = scheduler
self.armirq = armirq
self.physmem = physmem
self.index = index
self.reset()
def reset(self):
self.sha1 = pyemu.SHA1()
self.control = 0
self.srcaddr = 0
def read(self, addr):
if addr == SHA_CTRL: return self.control
elif addr == SHA_SRC: return self.srcaddr
elif addr == SHA_H0: return self.sha1.h0
elif addr == SHA_H1: return self.sha1.h1
elif addr == SHA_H2: return self.sha1.h2
elif addr == SHA_H3: return self.sha1.h3
elif addr == SHA_H4: return self.sha1.h4
print("SHA READ 0x%X at %08X" %(addr, self.scheduler.pc()))
return 0
def write(self, addr, value):
if addr == SHA_CTRL:
if value & 0x80000000:
blocks = (value & 0x3FF) + 1
for i in range(blocks):
data = self.physmem.read(self.srcaddr, 0x40)
self.srcaddr += 0x40
self.sha1.process_block(data)
if value & 0x40000000:
self.trigger_interrupt()
self.control = value & ~0x80000000
else:
self.reset()
elif addr == SHA_SRC: self.srcaddr = value
elif addr == SHA_H0: self.sha1.h0 = value
elif addr == SHA_H1: self.sha1.h1 = value
elif addr == SHA_H2: self.sha1.h2 = value
elif addr == SHA_H3: self.sha1.h3 = value
elif addr == SHA_H4: self.sha1.h4 = value
else:
print("SHA WRITE 0x%X %08X at %08X" %(addr, value, self.scheduler.pc()))
def trigger_interrupt(self):
if self.index == 0: self.armirq.trigger_irq_all(3)
else: self.armirq.trigger_irq_lt(9)
LATTE_START = 0xD000000
LATTE_END = 0xD001000
LT_TIMER = 0xD000010
LT_ALARM = 0xD000014
LT_AHB_WDG_CONFIG = 0xD00004C
LT_AHB_DMA_STATUS = 0xD000050
LT_AHB_CPU_STATUS = 0xD000054
LT_ERROR = 0xD000058
LT_ERROR_MASK = 0xD00005C
LT_MEMIRR = 0xD000060
LT_AHBPROT = 0xD000064
LT_EXICTRL = 0xD000070
LT_D800074 = 0xD000074
LT_D800088 = 0xD000088
LT_D800180 = 0xD000180
LT_COMPAT_MEMCTRL_WORKAROUND = 0xD000188
LT_BOOT0 = 0xD00018C
LT_CLOCKINFO = 0xD000190
LT_RESETS_COMPAT = 0xD000194
LT_CLOCKGATE_COMPAT = 0xD000198
LT_D8001D0 = 0xD0001D0
LT_IOPOWER = 0xD0001DC
LT_IOSTRENGTH_CTRL0 = 0xD0001E0
LT_IOSTRENGTH_CTRL1 = 0xD0001E4
LT_ACRCLK_STRENGTH_CTRL = 0xD0001E8
LT_OTPCMD = 0xD0001EC
LT_OTPDATA = 0xD0001F0
LT_D800204 = 0xD000204
LT_ASICREV_ACR = 0xD000214
LT_INTSR_AHBALL_PPC0 = 0xD000440
LT_INTSR_AHBLT_PPC0 = 0xD000444
LT_INTMR_AHBALL_PPC0 = 0xD000448
LT_INTMR_AHBLT_PPC0 = 0xD00044C
LT_INTSR_AHBALL_PPC1 = 0xD000450
LT_INTSR_AHBLT_PPC1 = 0xD000454
LT_INTMR_AHBALL_PPC1 = 0xD000458
LT_INTMR_AHBLT_PPC1 = 0xD00045C
LT_INTSR_AHBALL_PPC2 = 0xD000460
LT_INTSR_AHBLT_PPC2 = 0xD000464
LT_INTMR_AHBALL_PPC2 = 0xD000468
LT_INTMR_AHBLT_PPC2 = 0xD00046C
LT_INTSR_AHBALL_ARM = 0xD000470
LT_INTSR_AHBLT_ARM = 0xD000474
LT_INTMR_AHBALL_ARM = 0xD000478
LT_INTMR_AHBLT_ARM = 0xD00047C
LT_INTMR_AHBALL_ARM2x = 0xD000480
LT_INTMR_AHBLT_ARM2x = 0xD000484
LT_AHB2_DMA_STATUS = 0xD0004A4
LT_AHB2_CPU_STATUS = 0xD0004A8
LT_D800500 = 0xD000500
LT_D800504 = 0xD000504
LT_OTPPROT = 0xD000510
LT_ASICREV_CCR = 0xD0005A0
LT_DEBUG = 0xD0005A4
LT_COMPAT_MEMCTRL_STATE = 0xD0005B0
LT_IOP2X = 0xD0005BC
LT_IOSTRENGTH_CTRL2 = 0xD0005C8
LT_D8005CC = 0xD0005CC
LT_RESETS = 0xD0005E0
LT_RESETS_AHMN = 0xD0005E4
LT_SYSPLL_CFG = 0xD0005EC
LT_ABIF_CPLTL_OFFSET = 0xD000620
LT_ABIF_CPLTL_DATA = 0xD000624
LT_D800628 = 0xD000628
LT_60XE_CFG = 0xD000640
LT_D800660 = 0xD000660
LT_GPIO_START = 0xD0000C0
LT_GPIO_END = 0xD000100
LT_IPC_PPC0_START = 0xD000400
LT_IPC_PPC0_END = 0xD000410
LT_IPC_PPC1_START = 0xD000410
LT_IPC_PPC1_END = 0xD000420
LT_IPC_PPC2_START = 0xD000420
LT_IPC_PPC2_END = 0xD000430
LT_IRQ_PPC0_START = 0xD000440
LT_IRQ_PPC0_END = 0xD000450
LT_IRQ_PPC1_START = 0xD000450
LT_IRQ_PPC1_END = 0xD000460
LT_IRQ_PPC2_START = 0xD000460
LT_IRQ_PPC2_END = 0xD000470
LT_IRQ_ARM_START = 0xD000470
LT_IRQ_ARM_END = 0xD000488
LT_GPIO2_START = 0xD000520
LT_GPIO2_END = 0xD000560
BUS_CLOCK_SPEED = 248625000
TIMER_SPEED = 0x1DA36E
HARDWARE_VERSION_ACR = 0x21
HARDWARE_VERSION_CCR = 0xCAFE0060
class OTPController:
def __init__(self, scheduler):
with open("otp.bin", "rb") as f:
self.data = struct.unpack(">256I", f.read())
self.scheduler = scheduler
def read(self, bank, index):
return self.data[bank * 0x20 + index]
class SEEPROMController:
LISTEN = 0
WRITE = 1
POST_WRITE = 2
POST_POST_WRITE = 3
def __init__(self, scheduler):
with open("seeprom.bin", "rb") as f:
self.data = list(struct.unpack(">256H", f.read()))
self.state = self.LISTEN
self.scheduler = scheduler
def init_transfer(self):
self.index = 0
self.pin_state = 0
self.bits = 11
self.offset = 0
def update_pin(self):
if self.bits:
self.index = (self.index << 1) | self.pin_state
self.bits -= 1
if not self.bits:
if self.state == self.LISTEN:
self.handle_command(self.index)
elif self.state == self.WRITE:
self.handle_write(self.index)
elif self.state == self.POST_WRITE:
self.handle_write_done()
else:
self.state = self.LISTEN
else:
self.pin_state = (self.output >> (self.output_size - 1)) & 1
self.output <<= 1
def read_value(self, index):
return self.data[index]
def handle_command(self, value):
if self.index & ~0xC0 == 0x400: pass #Control
elif self.index & 0xF00 == 0x500: #Write
self.bits = 16
self.index = 0
self.offset = self.index & 0xFF
self.state = self.WRITE
elif self.index & 0xF00 == 0x600: #Read
self.output = self.read_value(self.index & 0xFF)
self.output_size = 16
else:
print("SEEPROM CMD 0x%X at %08X" %(self.index, self.scheduler.pc()))
def handle_write(self, value):
self.data[self.offset] = value
self.bits = 2
self.state = self.POST_WRITE
def handle_write_done(self):
self.output = 1
self.output_size = 1
self.bits = 2
self.state = self.POST_POST_WRITE
PIN_DWIFI_MODE = 1
PIN_ESP10_WORKAROUND = 5
PIN_9 = 9
PIN_EEPROM_CS = 10
PIN_EEPROM_SK = 11
PIN_EEPROM_DO = 12
PIN_EEPROM_DI = 13
PIN_AV0_I2C_CLOCK = 14
PIN_AV0_I2C_DATA = 15
PIN_AV1_I2C_CLOCK = 24
PIN_AV1_I2C_DATA = 25
PIN_BLUETOOTH_MODE = 27
PIN_WIFI_MODE = 29
PIN_31 = 31
class GPIOGroup1:
def __init__(self, scheduler, gpio):
self.seeprom = SEEPROMController(scheduler)
self.scheduler = scheduler
self.gpio = gpio
def read(self, espresso):
return self.seeprom.pin_state << PIN_EEPROM_DI
def write(self, pin, state, espresso):
if pin == PIN_DWIFI_MODE: pass
elif pin == PIN_ESP10_WORKAROUND: pass
elif pin == PIN_9: pass
elif pin == PIN_EEPROM_CS:
if state == 1:
self.seeprom.init_transfer()
elif pin == PIN_EEPROM_SK:
if state == 1:
self.seeprom.update_pin()
elif pin == PIN_EEPROM_DO:
self.seeprom.pin_state = state
elif pin == PIN_AV0_I2C_CLOCK: pass
elif pin == PIN_AV0_I2C_DATA: pass
elif pin == PIN_AV1_I2C_CLOCK: pass
elif pin == PIN_AV1_I2C_DATA: pass
elif pin == PIN_BLUETOOTH_MODE: pass
elif pin == PIN_WIFI_MODE: pass
elif pin == PIN_31: pass
else:
print("GPIO PIN WRITE %i %i at %08X" %(pin, state, self.scheduler.pc()))
PIN_AV_RESET = 6
class GPIOGroup2:
def __init__(self, scheduler, gpio):
self.scheduler = scheduler
self.gpio = gpio
def read(self, espresso):
return 0
def write(self, pin, state, espresso):
if pin == PIN_AV_RESET: pass
else:
print("GPIO2 PIN WRITE %i %i at %08X" %(pin, state, self.scheduler.pc()))
LT_GPIOE_OUT = 0
LT_GPIOE_DIR = 4
LT_GPIOE_IN = 8
LT_GPIOE_INTLVL = 0xC
LT_GPIOE_INTFLAG = 0x10
LT_GPIOE_INTMASK = 0x14
LT_GPIOE_INMIR = 0x18
LT_GPIO_ENABLE = 0x1C
LT_GPIO_OUT = 0x20
LT_GPIO_DIR = 0x24
LT_GPIO_IN = 0x28
LT_GPIO_INTLVL = 0x2C
LT_GPIO_INTFLAG = 0x30
LT_GPIO_INTMASK = 0x34
LT_GPIO_INMIR = 0x38
LT_GPIO_OWNER = 0x3C
class GPIOController:
def __init__(self, scheduler):
self.scheduler = scheduler
self.gpioe_dir = 0
self.gpioe_out = 0
self.gpioe_intmask = 0
self.gpioe_intflag = 0
self.gpioe_intlvl = 0
self.gpio_dir = 0
self.gpio_enabled = 0xFFFFFFFF
self.gpio_out = 0
self.gpio_intmask = 0
self.gpio_owner = 0
self.gpio_intflag = 0
self.gpio_intlvl = 0
self.group = None
def set_group(self, group): self.group = group
def read(self, addr):
if addr == LT_GPIOE_OUT: return self.gpioe_out
elif addr == LT_GPIOE_DIR: return self.gpioe_dir
elif addr == LT_GPIOE_INTLVL: return self.gpioe_intlvl
elif addr == LT_GPIOE_INTFLAG: return self.gpioe_intflag
elif addr == LT_GPIOE_INTMASK: return self.gpioe_intmask
elif addr == LT_GPIO_ENABLE: return self.gpio_enabled
elif addr == LT_GPIO_OUT: return self.gpio_out
elif addr == LT_GPIO_DIR: return self.gpio_dir
elif addr == LT_GPIO_IN: return self.group.read(False)
elif addr == LT_GPIO_INTLVL: return self.gpio_intlvl
elif addr == LT_GPIO_INTFLAG: return self.gpio_intflag
elif addr == LT_GPIO_INTMASK: return self.gpio_intmask
elif addr == LT_GPIO_OWNER: return self.gpio_owner
print("GPIO READ 0x%X at %08X" %(addr, self.scheduler.pc()))
return 0
def write(self, addr, value):
if addr == LT_GPIOE_OUT:
for i in range(32):
if self.gpio_owner & (1 << i):
if value & (1 << i) != self.gpioe_out & (1 << i):
self.group.write(i, (value >> i) & 1, True)
self.gpioe_out = value
elif addr == LT_GPIOE_DIR: self.gpioe_dir = value
elif addr == LT_GPIOE_INTLVL: self.gpioe_intlvl = value
elif addr == LT_GPIOE_INTFLAG: self.gpioe_intflag &= ~value
elif addr == LT_GPIOE_INTMASK: self.gpioe_intmask = value
elif addr == LT_GPIO_ENABLE: self.gpio_enabled = value
elif addr == LT_GPIO_OUT:
for i in range(32):
if value & (1 << i) != self.gpio_out & (1 << i):
self.group.write(i, (value >> i) & 1, False)
self.gpio_out = value
elif addr == LT_GPIO_DIR: self.gpio_dir = value
elif addr == LT_GPIO_INTLVL: self.gpio_intlvl = value
elif addr == LT_GPIO_INTFLAG: self.gpio_intflag &= ~value
elif addr == LT_GPIO_INTMASK: self.gpio_intmask = value
elif addr == LT_GPIO_OWNER: self.gpio_owner = value
else:
print("GPIO WRITE 0x%X %08X at %08X" %(addr, value, self.scheduler.pc()))
def trigger_interrupt(self, type, espresso):
if espresso: self.gpioe_intflag |= 1 << type
else: self.gpio_intflag |= 1 << type
def check_interrupts_ppc(self): return self.gpioe_intflag & self.gpioe_intmask
def check_interrupts_arm(self): return self.gpio_intflag & self.gpio_intmask
I2C_CLOCK = 0
I2C_WRITE_DATA = 1
I2C_WRITE_CTRL = 2
I2C_READ_DATA = 3
I2C_INT_MASK = 4
I2C_INT_STATE = 5
LT_I2C_REGS = {
0xD000570: I2C_CLOCK,
0xD000574: I2C_WRITE_DATA,
0xD000578: I2C_WRITE_CTRL,
0xD00057C: I2C_READ_DATA,
0xD000580: I2C_INT_MASK,
0xD000584: I2C_INT_STATE
}
LT_I2C_REGS_PPC = {
0xD000068: I2C_INT_MASK,
0xD00006C: I2C_INT_STATE,
0xD000250: I2C_CLOCK,
0xD000254: I2C_WRITE_DATA,
0xD000258: I2C_WRITE_CTRL,
0xD00025C: I2C_READ_DATA
}
class I2CController:
def __init__(self, scheduler, gpio2, espresso):
self.scheduler = scheduler
self.gpio2 = gpio2
self.espresso = espresso
self.readbuf = b""
self.readoffs = 0
self.readsize = False
self.writebuf = b""
self.writeval = 0
self.offset = 0
self.clock = 0
self.int_mask = 0
self.int_state = 0
self.readint = 5 if espresso else 0
self.writeint = 6 if espresso else 1
self.av_int_mask = 0
self.av_int_info = [0] * 6
def read(self, addr):
if addr == I2C_CLOCK: return self.clock
elif addr == I2C_WRITE_DATA: return self.writeval
elif addr == I2C_READ_DATA:
if self.readsize:
self.readsize = False
return len(self.readbuf) << 16
self.readoffs += 1
return self.readbuf[self.readoffs]
elif addr == I2C_WRITE_CTRL: return 0
elif addr == I2C_INT_MASK: return self.int_mask
elif addr == I2C_INT_STATE: return self.int_state
raise RuntimeError("Read from I2C register %i" %addr)
def write(self, addr, value):
if addr == I2C_CLOCK: self.clock = value
elif addr == I2C_WRITE_DATA: self.writeval = value
elif addr == I2C_WRITE_CTRL:
if value & 1:
self.writebuf += bytes([self.writeval & 0xFF])
if self.writeval & 0x100:
self.handle_data(self.writebuf)
self.writebuf = b""
elif addr == I2C_INT_MASK: self.int_mask = value
elif addr == I2C_INT_STATE: self.int_state &= ~value
else:
raise RuntimeError("Write to I2C register %i" %addr)
def handle_data(self, data):
slave = data[0] >> 1
read = data[0] & 1
if read:
self.readbuf = self.read_data(slave, self.offset, len(data) - 1)
self.readoffs = -1
self.readsize = not self.espresso #This is weird
self.trigger_interrupt(self.readint)
else:
if len(data) > 2:
self.write_data(slave, data[1], data[2:])
self.offset = data[1]
self.trigger_interrupt(self.writeint)
def read_data(self, slave, offset, length):
if slave == 0x38 and offset == 0x90: return bytes([self.av_int_mask])
elif slave == 0x38 and 0x91 <= offset <= 0x97: return bytes([self.av_int_info[offset - 0x91]])
print("I2C READ 0x%X 0x%X %i" %(slave, offset, length))
return bytes(length)
def write_data(self, slave, offset, data):
if slave == 0x3D and offset == 0x89:
self.av_int_mask |= 0x10
self.av_int_info[4] = 0
self.gpio2.trigger_interrupt(4, True)
else:
print("I2C WRITE 0x%X 0x%X %s" %(slave, offset, data.hex()))
def trigger_interrupt(self, type):
self.int_state |= 1 << type
def check_interrupts(self):
return self.int_state & self.int_mask
class ASICBusController:
def __init__(self, scheduler):
self.scheduler = scheduler
self.pll_data = [0xC, 0x800, 0x1C2, 7, 0, 0, 0x100, 0x40, 0xC800]
self.usbpll_data = [0x20, 3, 0x1200, 0x3F, 0xC120]
self.gfxpll_data = [0x1200, 0x20, 0xA, 0x800, 0xD, 0x800, 0x81C2, 0, 0x4002, 0]
self.satapll_data = [0] * 9
def set_offset(self, offset): self.offset = offset
def get_data(self):
if 0x3000010 <= self.offset < 0x3000022:
return self.pll_data[(self.offset - 0x3000010) // 2]
elif 0x4000024 <= self.offset < 0x400002E:
return self.usbpll_data[(self.offset - 0x4000024) // 2]
elif 0x878 <= self.offset < 0x88C:
return self.gfxpll_data[(self.offset - 0x878) // 2]
elif self.offset == 0x1000000:
return 0x54
elif 0x4000010 <= self.offset < 0x4000022:
return self.satapll_data[(self.offset - 0x4000010) // 2]
elif self.offset >> 24 == 0xC0: return 0
print("ASIC BIF READ 0x%08X at %08X" %(self.offset, self.scheduler.pc()))
return 0
def write(self, value):
if 0x3000010 <= self.offset < 0x3000022:
self.pll_data[(self.offset - 0x3000010) // 2] = value
elif 0x4000024 <= self.offset < 0x400002E:
self.usbpll_data[(self.offset - 0x4000024) // 2] = value
elif 0x4000010 <= self.offset < 0x4000022:
self.satapll_data[(self.offset - 0x4000010) // 2] = value
elif self.offset >> 24 == 0xC0: pass
else:
print("ASIC BIF WRITE 0x%08X %08X at %08X" %(self.offset, value, self.scheduler.pc()))
LT_IPC_PPCMSG = 0
LT_IPC_PPCCTRL = 4
LT_IPC_ARMMSG = 8
LT_IPC_ARMCTRL = 0xC
class IPCController:
def __init__(self, scheduler):
self.scheduler = scheduler
self.ppcmsg = 0
self.armmsg = 0
self.x1 = self.x2 = 0
self.y1 = self.y2 = 0
self.ix1 = self.ix2 = 0
self.iy1 = self.iy2 = 0
def read(self, addr):
if addr == LT_IPC_PPCMSG: return self.ppcmsg
elif addr == LT_IPC_ARMMSG: return self.armmsg
elif addr == LT_IPC_PPCCTRL:
return self.x1 | (self.y2 << 1) | (self.y1 << 2) | \
(self.x2 << 3) | (self.iy1 << 4) | (self.iy2 << 5)
elif addr == LT_IPC_ARMCTRL:
return self.y1 | (self.x2 << 1) | (self.x1 << 2) | \
(self.y2 << 3) | (self.ix1 << 4) | (self.ix2 << 5)
def write(self, addr, value):
if addr == LT_IPC_PPCMSG: self.ppcmsg = value
elif addr == LT_IPC_ARMMSG: self.armmsg = value
elif addr == LT_IPC_PPCCTRL:
if value & 1: self.x1 = 1
if value & 2: self.y2 = 0
if value & 4: self.y1 = 0
if value & 8: self.x2 = 1
self.iy1 = (value >> 4) & 1
self.iy2 = (value >> 5) & 1
elif addr == LT_IPC_ARMCTRL:
if value & 1: self.y1 = 1
if value & 2: self.x2 = 0
if value & 4: self.x1 = 0
if value & 8: self.y2 = 1
self.ix1 = (value >> 4) & 1
self.ix2 = (value >> 5) & 1
def check_interrupts_ppc(self): return (self.y1 and self.iy1) or (self.y2 and self.iy2)
def check_interrupts_arm(self): return (self.x1 and self.ix1) or (self.x2 and self.ix2)
LT_INTSR_AHBALL = 0
LT_INTSR_AHBLT = 4
LT_INTMR_AHBALL = 8
LT_INTMR_AHBLT = 0xC
LT_INTMR_AHBALL_2X = 0x10
LT_INTMR_AHBLT_2X = 0x14
class IRQController:
def __init__(self):
self.intsr_ahball = 0
self.intsr_ahblt = 0
self.intmr_ahball = 0
self.intmr_ahblt = 0
def read(self, addr):
if addr == LT_INTSR_AHBALL: return self.intsr_ahball
elif addr == LT_INTSR_AHBLT: return self.intsr_ahblt
elif addr == LT_INTMR_AHBALL: return self.intmr_ahball
elif addr == LT_INTMR_AHBLT: return self.intmr_ahblt
def write(self, addr, value):
if addr == LT_INTSR_AHBALL: self.intsr_ahball &= ~value
elif addr == LT_INTSR_AHBLT: self.intsr_ahblt &= ~value
elif addr == LT_INTMR_AHBALL: self.intmr_ahball = value
elif addr == LT_INTMR_AHBLT: self.intmr_ahblt = value
def trigger_irq_all(self, type): self.intsr_ahball |= (1 << type)
def trigger_irq_lt(self, type): self.intsr_ahblt |= (1 << type)
def is_triggered_all(self, type): return self.intsr_ahball & self.intmr_ahball & (1 << type)
def is_triggered_lt(self, type): return self.intsr_ahblt & self.intmr_ahblt & (1 << type)
def check_interrupts(self):
self.update_interrupts()
return self.intsr_ahball & self.intmr_ahball or self.intsr_ahblt & self.intmr_ahblt
def update_interrupts(self): pass
class IRQControllerPPC(IRQController):
def __init__(self, ipc, gpio, gpio2, i2c, index):
super().__init__()
self.ipc = ipc
self.gpio = gpio
self.gpio2 = gpio2
self.i2c = i2c
self.index = index
def update_interrupts(self):
if self.ipc.check_interrupts_ppc():
self.trigger_irq_lt(30 - 2 * self.index)
if self.gpio.check_interrupts_ppc() or self.gpio2.check_interrupts_ppc():
self.trigger_irq_all(10)
if self.i2c.check_interrupts():
self.trigger_irq_lt(13)
class IRQControllerARM(IRQController):
def __init__(self, ipc, gpio, gpio2, i2c):
super().__init__()
self.intmr_ahball_2x = 0
self.intmr_ahblt_2x = 0
self.ipc = ipc
self.gpio = gpio
self.gpio2 = gpio2
self.i2c = i2c
def read(self, addr):
if addr == LT_INTMR_AHBALL_2X: return self.intmr_ahball_2x
elif addr == LT_INTMR_AHBLT_2X: return self.intmr_ahblt_2x
return super().read(addr)
def write(self, addr, value):
if addr == LT_INTMR_AHBALL_2X: self.intmr_ahball_2x = value
elif addr == LT_INTMR_AHBLT_2X: self.intmr_ahblt_2x = value
else: super().write(addr, value)
def update_interrupts(self):
for i in range(3):
if self.ipc[i].check_interrupts_arm():
self.trigger_irq_lt(31 - 2 * i)
if self.gpio.check_interrupts_arm() or self.gpio2.check_interrupts_arm():
self.trigger_irq_all(11)
if self.i2c.check_interrupts():
self.trigger_irq_lt(14)
class LatteController:
def __init__(self, scheduler, debug=False):
self.ipc = [IPCController(scheduler) for i in range(3)]
self.gpio = GPIOController(scheduler)
self.gpio.set_group(GPIOGroup1(scheduler, self.gpio))
self.gpio2 = GPIOController(scheduler)
self.gpio2.set_group(GPIOGroup2(scheduler, self.gpio2))
self.i2c = I2CController(scheduler, self.gpio2, False)
self.i2c_ppc = I2CController(scheduler, self.gpio2, True)
self.irq_ppc = [IRQControllerPPC(self.ipc[i], self.gpio, self.gpio2, self.i2c_ppc, i) for i in range(3)]
self.irq_arm = IRQControllerARM(self.ipc, self.gpio, self.gpio2, self.i2c)
self.otp = OTPController(scheduler)
self.asicbus = ASICBusController(scheduler)
self.timer = 0
self.alarm = 0
self.ahb_wdg_config = 0
self.error = 0
self.error_mask = 0
self.memirr = 0
self.ahbprot = 0
self.exi_ctrl = 0
self.d800074 = 0
self.d800180 = 0
self.compat_memctrl_workaround = 0
self.boot0 = 0
self.clockinfo = 0
self.resets_compat = 0
self.clockgate_compat = 0
self.d8001d0 = 0
self.iopower = 0
self.iostrength_ctrl0 = 0
self.iostrength_ctrl1 = 0
self.acrclk_strength_ctrl = 0
self.otpcmd = 0
self.otpdata = 0
self.d800204 = 0
self.d800500 = 0
self.d800504 = 0
self.debug = 0x80000000 * debug + 0x20000000
self.compat_memctrl_state = 0
self.iop2x = 0
self.iostrength_ctrl2 = 0
self.d8005cc = 0
self.resets = 0
self.resets_ahmn = 0
self.syspll_cfg = 0
self.d800628 = 0
self.cfg_60xe = 0
self.scheduler = scheduler
def read(self, addr):
if addr == LT_TIMER: return self.timer
elif LT_IRQ_PPC0_START <= addr < LT_IRQ_PPC0_END: return self.irq_ppc[0].read(addr - LT_IRQ_PPC0_START)
elif LT_IRQ_PPC1_START <= addr < LT_IRQ_PPC1_END: return self.irq_ppc[1].read(addr - LT_IRQ_PPC1_START)
elif LT_IRQ_PPC2_START <= addr < LT_IRQ_PPC2_END: return self.irq_ppc[2].read(addr - LT_IRQ_PPC2_START)
elif LT_IRQ_ARM_START <= addr < LT_IRQ_ARM_END: return self.irq_arm.read(addr - LT_IRQ_ARM_START)
elif LT_IPC_PPC0_START <= addr < LT_IPC_PPC0_END: return self.ipc[0].read(addr - LT_IPC_PPC0_START)
elif LT_IPC_PPC1_START <= addr < LT_IPC_PPC1_END: return self.ipc[1].read(addr - LT_IPC_PPC1_START)
elif LT_IPC_PPC2_START <= addr < LT_IPC_PPC2_END: return self.ipc[2].read(addr - LT_IPC_PPC2_START)
elif addr == LT_AHB_WDG_CONFIG: return self.ahb_wdg_config
elif addr == LT_ERROR: return self.error
elif addr == LT_ERROR_MASK: return self.error_mask
elif addr == LT_MEMIRR: return self.memirr
elif addr == LT_AHBPROT: return self.ahbprot
elif addr == LT_EXICTRL: return self.exi_ctrl
elif addr == LT_D800074: return self.d800074
elif LT_GPIO_START <= addr < LT_GPIO_END: return self.gpio.read(addr - LT_GPIO_START)
elif addr == LT_D800180: return self.d800180
elif addr == LT_COMPAT_MEMCTRL_WORKAROUND: return self.compat_memctrl_workaround
elif addr == LT_BOOT0: return self.boot0
elif addr == LT_CLOCKINFO: return self.clockinfo
elif addr == LT_RESETS_COMPAT: return self.resets_compat
elif addr == LT_CLOCKGATE_COMPAT: return self.clockgate_compat
elif addr == LT_D8001D0: return self.d8001d0
elif addr == LT_IOPOWER: return self.iopower
elif addr == LT_IOSTRENGTH_CTRL0: return self.iostrength_ctrl0
elif addr == LT_IOSTRENGTH_CTRL1: return self.iostrength_ctrl1
elif addr == LT_ACRCLK_STRENGTH_CTRL: return self.acrclk_strength_ctrl
elif addr == LT_OTPCMD: return self.otpcmd
elif addr == LT_OTPDATA: return self.otpdata
elif addr == LT_D800204: return self.d800204
elif addr == LT_ASICREV_ACR: return HARDWARE_VERSION_ACR
elif addr == LT_D800500: return self.d800500
elif addr == LT_D800504: return self.d800504
elif LT_GPIO2_START <= addr < LT_GPIO2_END: return self.gpio2.read(addr - LT_GPIO2_START)
elif addr == LT_ASICREV_CCR: return HARDWARE_VERSION_CCR
elif addr == LT_DEBUG: return self.debug
elif addr == LT_COMPAT_MEMCTRL_STATE: return self.compat_memctrl_state
elif addr == LT_IOP2X: return self.iop2x
elif addr == LT_IOSTRENGTH_CTRL2: return self.iostrength_ctrl2
elif addr == LT_D8005CC: return self.d8005cc
elif addr == LT_RESETS: return self.resets
elif addr == LT_RESETS_AHMN: return self.resets_ahmn
elif addr == LT_SYSPLL_CFG: return self.syspll_cfg
elif addr == LT_ABIF_CPLTL_DATA: return self.asicbus.get_data()
elif addr == LT_D800628: return self.d800628
elif addr == LT_60XE_CFG: return self.cfg_60xe
elif addr in LT_I2C_REGS: return self.i2c.read(LT_I2C_REGS[addr])
elif addr in LT_I2C_REGS_PPC: return self.i2c_ppc.read(LT_I2C_REGS_PPC[addr])
print("LATTE READ 0x%X at %08X" %(addr, self.scheduler.pc()))
return 0
def write(self, addr, value):
if addr == LT_TIMER: self.timer = value
elif addr == LT_ALARM: self.alarm = value
elif addr == LT_AHB_WDG_CONFIG: self.ahb_wdg_config = value
elif addr == LT_AHB_DMA_STATUS: pass
elif addr == LT_AHB_CPU_STATUS: pass
elif addr == LT_ERROR: self.error = value & self.error_mask
elif addr == LT_ERROR_MASK: self.error_mask = value
elif addr == LT_MEMIRR: self.memirr = value
elif addr == LT_AHBPROT: self.ahbprot = value
elif addr == LT_EXICTRL: self.exi_ctrl = value
elif addr == LT_D800074: self.d800074 = value
elif addr == LT_D800088: pass
elif LT_GPIO_START <= addr < LT_GPIO_END: self.gpio.write(addr - LT_GPIO_START, value)
elif addr == LT_D800180: self.d800180 = value
elif addr == LT_COMPAT_MEMCTRL_WORKAROUND: self.compat_memctrl_workaround = value
elif addr == LT_BOOT0: self.boot0 = value
elif addr == LT_RESETS_COMPAT: self.resets_compat = value
elif addr == LT_CLOCKGATE_COMPAT: self.clockgate_compat = value
elif addr == LT_D8001D0: self.d8001d0 = value
elif addr == LT_IOPOWER: self.iopower = value
elif addr == LT_IOSTRENGTH_CTRL0: self.iostrength_ctrl0 = value
elif addr == LT_IOSTRENGTH_CTRL1: self.iostrength_ctrl1 = value
elif addr == LT_ACRCLK_STRENGTH_CTRL: self.acrclk_strength_ctrl = value
elif addr == LT_OTPCMD:
self.otpcmd = value
if value & 0x80000000:
self.otpdata = self.otp.read((value >> 8) & 7, value & 0x1F)
elif addr == LT_D800204: self.d800204 = value
elif LT_IPC_PPC0_START <= addr < LT_IPC_PPC0_END: self.ipc[0].write(addr - LT_IPC_PPC0_START, value)
elif LT_IPC_PPC1_START <= addr < LT_IPC_PPC1_END: self.ipc[1].write(addr - LT_IPC_PPC1_START, value)
elif LT_IPC_PPC2_START <= addr < LT_IPC_PPC2_END: self.ipc[2].write(addr - LT_IPC_PPC2_START, value)
elif LT_IRQ_PPC0_START <= addr < LT_IRQ_PPC0_END: self.irq_ppc[0].write(addr - LT_IRQ_PPC0_START, value)
elif LT_IRQ_PPC1_START <= addr < LT_IRQ_PPC1_END: self.irq_ppc[1].write(addr - LT_IRQ_PPC1_START, value)
elif LT_IRQ_PPC2_START <= addr < LT_IRQ_PPC2_END: self.irq_ppc[2].write(addr - LT_IRQ_PPC2_START, value)
elif LT_IRQ_ARM_START <= addr < LT_IRQ_ARM_END: self.irq_arm.write(addr - LT_IRQ_ARM_START, value)
elif addr == LT_AHB2_DMA_STATUS: pass
elif addr == LT_AHB2_CPU_STATUS: pass
elif addr == LT_D800500: self.d800500 = value
elif addr == LT_D800504: self.d800504 = value
elif addr == LT_OTPPROT: pass
elif LT_GPIO2_START <= addr < LT_GPIO2_END: self.gpio2.write(addr - LT_GPIO2_START, value)
elif addr == LT_DEBUG: self.debug = value
elif addr == LT_COMPAT_MEMCTRL_STATE: self.compat_memctrl_state = value
elif addr == LT_IOP2X:
self.iop2x = value | 4
self.irq_arm.trigger_irq_lt(12)
elif addr == LT_IOSTRENGTH_CTRL2: self.iostrength_ctrl2 = value
elif addr == LT_D8005CC: self.d8005cc = value
elif addr == LT_RESETS: self.resets = value
elif addr == LT_RESETS_AHMN: self.resets_ahmn = value
elif addr == LT_SYSPLL_CFG: self.syspll_cfg = value
elif addr == LT_ABIF_CPLTL_OFFSET: self.asicbus.set_offset(value)
elif addr == LT_ABIF_CPLTL_DATA: self.asicbus.write(value)
elif addr == LT_D800628: self.d800628 = value
elif addr == LT_60XE_CFG: self.cfg_60xe = value
elif addr == LT_D800660: pass
elif addr in LT_I2C_REGS: self.i2c.write(LT_I2C_REGS[addr], value)
elif addr in LT_I2C_REGS_PPC: self.i2c_ppc.write(LT_I2C_REGS_PPC[addr], value)
else:
print("LATTE WRITE 0x%X %08X at %08X" %(addr, value, self.scheduler.pc()))
def update_timer(self):
start = self.timer
end = self.timer + 400
if end >= 0x100000000:
if self.alarm > start or self.alarm <= end:
self.irq_arm.trigger_irq_all(0)
end &= 0xFFFFFFFF
else:
if start < self.alarm <= end:
self.irq_arm.trigger_irq_all(0)
self.timer = end
PI_CPU0_START = 0xC000078
PI_CPU0_END = 0xC000080
PI_CPU1_START = 0xC000080
PI_CPU1_END = 0xC000088
PI_CPU2_START = 0xC000088
PI_CPU2_END = 0xC000090
PI_INTSR = 0
PI_INTMR = 4
class PIController:
def __init__(self, scheduler, irq, gpu, dsp, index):
self.scheduler = scheduler
self.irq = irq
self.gpu = gpu
self.dsp = dsp
self.index = index
self.intsr = 0
self.intmr = 0
def read(self, addr):
if addr == PI_INTSR: return self.intsr
elif addr == PI_INTMR: return self.intmr
print("PI(%i) READ 0x%X at %08X" %(self.index, addr, self.scheduler.pc()))
return 0
def write(self, addr, value):
if addr == PI_INTSR: self.intsr &= ~value
elif addr == PI_INTMR: self.intmr = value
else:
print("PI(%i) WRITE 0x%X %08X at %08X" %(self.index, addr, value, self.scheduler.pc()))
def trigger_irq(self, type):
self.intsr |= 1 << type
def check_interrupts(self):
self.update_interrupts()
return self.intsr & self.intmr
def update_interrupts(self):
if self.irq.check_interrupts():
self.trigger_irq(24)
if self.gpu.check_interrupts():
self.trigger_irq(23)
if self.dsp.check_interrupts():
self.trigger_irq(6)
PAD_START = 0xC1E0000
PAD_END = 0xC200000
class PADController:
def __init__(self, scheduler):
self.scheduler = scheduler
def read(self, addr):
print("PAD READ 0x%X at %08X" %(addr, self.scheduler.pc()))
return 0
def write(self, addr, value):
print("PAD WRITE 0x%X %08X at %08X" %(addr, value, self.scheduler.pc()))
GPU_START = 0xC200000
GPU_END = 0xC300000
DC0_START = 0xC206000
DC0_END = 0xC206800
DC1_START = 0xC206800
DC1_END = 0xC207000
#From dc.rpl
DC_70 = 0x70
DC_9C = 0x9C
DC_INT_MASK = 0xDC
DC_4A0 = 0x4A0
class DCController:
def __init__(self, scheduler):
self.scheduler = scheduler
self.int_mask = 0
def read(self, addr):
if addr == DC_70: return 0x10000
elif addr == DC_9C: return 1
elif addr == DC_4A0: return 2
print("DC READ 0x%X at %08X" %(addr, self.scheduler.pc()))
return 0
def write(self, addr, value):
if addr == DC_INT_MASK: self.int_mask = value
else:
print("DC WRITE 0x%X %08X at %08X" %(addr, value, self.scheduler.pc()))
class PM4Processor:
STATE_NEXT = 0
STATE_BODY = 1
def __init__(self, scheduler, physmem):
self.scheduler = scheduler
self.physmem = physmem
self.state = self.STATE_NEXT
self.count = 0
self.body = []
self.opcode_handlers = {
0x3C: self.opcode_wait_reg_mem,
0x3D: self.opcode_mem_write,
0x43: self.opcode_surface_sync,
0x68: self.opcode_set_config_reg,
0x69: self.opcode_set_context_reg
}
def write(self, value):
if self.state == self.STATE_NEXT:
type = value >> 30
if type == 1: raise RuntimeError("Invalid PM4 packet type: 1")
elif type == 2: pass #Filler packet
else:
self.body = [value]
self.count = ((value >> 16) & 0x3FFF) + 1
self.state = self.STATE_BODY
elif self.state == self.STATE_BODY:
self.body.append(value)
self.count -= 1
if self.count == 0:
self.process()
self.state = self.STATE_NEXT
def process(self):
type = self.body[0] >> 30
if type == 0:
base_index = self.body[0] & 0xFFFF
print("PM4 packet type 0:%04X at %08X" %(base_index, self.scheduler.pc()))
else:
opcode = (self.body[0] >> 8) & 0xFF
if opcode in self.opcode_handlers:
self.opcode_handlers[opcode]()
else:
print("PM4 packet type 3:%02X at %08X" %(opcode, self.scheduler.pc()))
def print_opcode(self, opcode):
args = ", ".join(["%08X" %x for x in self.body[1:]])
print("PM4: %s(%s) at %08X" %(opcode, args, self.scheduler.pc()))
def opcode_wait_reg_mem(self): self.print_opcode("WAIT_REG_MEM")
def opcode_surface_sync(self): self.print_opcode("SURFACE_SYNC")
def opcode_set_config_reg(self): self.print_opcode("SET_CONFIG_REG")
def opcode_set_context_reg(self): self.print_opcode("SET_CONTEXT_REG")
def opcode_mem_write(self):
addr = self.body[1] & ~3
if self.body[2] & 0x40000:
self.physmem.write(addr, struct.pack(">I", self.body[3]))
else:
self.physmem.write(addr, struct.pack(">II", self.body[4], self.body[3]))
DRMDMA_RB_CNTL = 0xC20D000
DRMDMA_RB_BASE = 0xC20D004
DRMDMA_RB_RPTR = 0xC20D008
DRMDMA_RB_WPTR = 0xC20D00C
DRMDMA_RB_WPTR_POLL_CNTL = 0xC20D010
DRMDMA_RB_WPTR_POLL_ADDR_HI = 0xC20D014
DRMDMA_RB_WPTR_POLL_ADDR_LO = 0xC20D018
DRMDMA_RB_RPTR_ADDR_HI = 0xC20D01C
DRMDMA_RB_RPTR_ADDR_LO = 0xC20D020
DRMDMA_IB_CNTL = 0xC20D024
DRMDMA_IB_RPTR = 0xC20D028
DRMDMA_CNTL = 0xC20D02C
DRMDMA_STATUS = 0xC20D034
DRMDMA_IB_OFFSET = 0xC20D04C
DRMDMA_IB_BASE_LO = 0xC20D050
DRMDMA_IB_BASE_HI = 0xC20D054
DRMDMA_IB_SIZE = 0xC20D058
DRMDMA_FAULT_ADDR_LO = 0xC20D068
DRMDMA_FAULT_ADDR_HI = 0xC20D06C
DRMDMA_START = 0xC20D000
DRMDMA_END = 0xC20D800
class DMAController:
def __init__(self, scheduler, physmem, gpu):
self.scheduler = scheduler
self.physmem = physmem
self.gpu = gpu
self.rb_cntl = 0
self.rb_base = 0
self.rb_rptr = 0
self.rb_wptr = 0
self.rb_rptr_addr = 0
def read(self, addr):
if addr == DRMDMA_RB_CNTL: return self.rb_cntl
elif addr == DRMDMA_RB_BASE: return self.rb_base
elif addr == DRMDMA_RB_RPTR: return self.rb_rptr
elif addr == DRMDMA_RB_WPTR: return self.rb_wptr
elif addr == DRMDMA_STATUS: return 1
print("DMA READ 0x%X at %08X" %(addr, self.scheduler.pc()))
return 0
def write(self, addr, value):
if addr == DRMDMA_RB_CNTL: self.rb_cntl = value
elif addr == DRMDMA_RB_BASE: self.rb_base = value
elif addr == DRMDMA_RB_RPTR: self.rb_rptr = value
elif addr == DRMDMA_RB_WPTR: self.rb_wptr = value
elif addr == DRMDMA_RB_RPTR_ADDR_HI:
self.rb_rptr_addr = (self.rb_rptr_addr & 0xFFFFFFFF) | (value << 32)
elif addr == DRMDMA_RB_RPTR_ADDR_LO:
self.rb_rptr_addr = (self.rb_rptr_addr & ~0xFFFFFFFF) | value
else:
print("DMA WRITE 0x%X %08X at %08X" %(addr, value, self.scheduler.pc()))
return 0
def update(self):
self.state = 0
self.ptr = 0
if self.rb_rptr != self.rb_wptr:
self.rb_rptr = self.rb_wptr
self.physmem.write(self.rb_rptr_addr, struct.pack(">I", self.rb_rptr))
self.physmem.write(self.rb_rptr_addr - 0x20, struct.pack(">I", self.rb_rptr))
self.gpu.trigger_interrupt(0xE0)
#Interrupt handling
GPU_IH_RB_BASE = 0xC203E04
GPU_IH_RB_RPTR = 0xC203E08
GPU_IH_RB_WPTR_ADDR_LO = 0xC203E14
GPU_IH_CONTROL = 0xC203E18
GPU_IH_STATUS = 0xC203E20
#RLC
GPU_RLC_UCODE_ADDR = 0xC203F2C
GPU_RLC_UCODE_DATA = 0xC203F30
GPU_CP_RESET = 0xC208020
GPU_FLUSH = 0xC208500
#Command processor
GPU_CP_RB_BASE = 0xC20C100
GPU_CP_RB_RPTR_ADDR = 0xC20C10C
GPU_CP_RB_WPTR = 0xC20C114
GPU_CP_PFP_UCODE_ADDR = 0xC20C150
GPU_CP_PFP_UCODE_DATA = 0xC20C154
GPU_CP_ME_RAM_WADDR = 0xC20C15C
GPU_CP_ME_RAM_DATA = 0xC20C160
GPU_PM4_DATA = 0xC2C0000
class GPUController:
def __init__(self, scheduler, physmem):
self.scheduler = scheduler
self.scheduler.add_alarm(10000, self.trigger_vsync)
self.physmem = physmem
self.pm4 = PM4Processor(scheduler, physmem)
self.dc0 = DCController(scheduler)
self.dc1 = DCController(scheduler)
self.dma = DMAController(scheduler, physmem, self)
self.ih_rb_base = 0
self.ih_rb_rptr = 0
self.ih_rb_wptr_addr = 0
self.ih_control = 0
self.rlc_ucode_data = [0] * 0x400
self.rlc_ucode_addr = 0
self.cp_rb_base = 0
self.cp_rb_rptr_addr = 0
self.cp_rb_wptr = 0
self.cp_pfp_ucode_data = [0] * 0x350
self.cp_pfp_ucode_addr = 0
self.cp_me_ram_data = [0] * 0x550
self.cp_me_ram_waddr = 0
def read(self, addr):
if addr == GPU_RLC_UCODE_DATA:
value = self.rlc_ucode_data[self.rlc_ucode_addr]
self.rlc_ucode_addr += 1
return value
elif DC0_START <= addr < DC0_END: return self.dc0.read(addr - DC0_START)
elif DC1_START <= addr < DC1_END: return self.dc1.read(addr - DC1_START)
elif DRMDMA_START <= addr < DRMDMA_END: return self.dma.read(addr)
elif addr == GPU_IH_CONTROL: return self.ih_control
elif addr == GPU_IH_STATUS:
if self.ih_control & 1:
return 0
return 5
elif addr == GPU_FLUSH:
#Process command buffers here?
self.physmem.write(self.cp_rb_rptr_addr, struct.pack(">H", self.cp_rb_wptr))
self.dma.update()
return 0
elif addr == GPU_CP_PFP_UCODE_DATA:
value = self.cp_pfp_ucode_data[self.cp_pfp_ucode_addr]
self.cp_pfp_ucode_addr += 1
return value
elif addr == GPU_CP_ME_RAM_DATA:
value = self.cp_me_ram_data[self.cp_me_ram_waddr]
self.cp_me_ram_waddr += 1
return value
print("GPU READ 0x%X at %08X" %(addr, self.scheduler.pc()))
return 0
def write(self, addr, value):
if addr == GPU_IH_RB_BASE: self.ih_rb_base = value << 8
elif addr == GPU_IH_RB_RPTR: self.ih_rb_rptr = value
elif addr == GPU_IH_RB_WPTR_ADDR_LO: self.ih_rb_wptr_addr = value
elif addr == GPU_IH_CONTROL: self.ih_control = value
elif addr == GPU_RLC_UCODE_ADDR: self.rlc_ucode_addr = value
elif addr == GPU_RLC_UCODE_DATA:
self.rlc_ucode_data[self.rlc_ucode_addr] = value
self.rlc_ucode_addr += 1
elif DC0_START <= addr < DC0_END: self.dc0.write(addr - DC0_START, value)
elif DC1_START <= addr < DC1_END: self.dc1.write(addr - DC1_START, value)
elif DRMDMA_START <= addr < DRMDMA_END: self.dma.write(addr, value)
elif addr == GPU_CP_RESET: pass
elif addr == GPU_CP_RB_BASE: self.cp_rb_base = value << 8
elif addr == GPU_CP_RB_RPTR_ADDR: self.cp_rb_rptr_addr = value
elif addr == GPU_CP_RB_WPTR: self.cp_rb_wptr = value
elif addr == GPU_CP_PFP_UCODE_ADDR: self.cp_pfp_ucode_addr = value
elif addr == GPU_CP_PFP_UCODE_DATA:
self.cp_pfp_ucode_data[self.cp_pfp_ucode_addr] = value
self.cp_pfp_ucode_addr += 1
elif addr == GPU_CP_ME_RAM_WADDR: self.cp_me_ram_waddr = value
elif addr == GPU_CP_ME_RAM_DATA:
self.cp_me_ram_data[self.cp_me_ram_waddr] = value
self.cp_me_ram_waddr += 1
elif addr == GPU_PM4_DATA: self.pm4.write(value)
else:
print("GPU WRITE 0x%X %08X at %08X" %(addr, value, self.scheduler.pc()))
def get_ih_rb_wptr(self): return struct.unpack(">I", self.physmem.read(self.ih_rb_wptr_addr, 4))[0]
def set_ih_rb_wptr(self, pos): self.physmem.write(self.ih_rb_wptr_addr, struct.pack(">I", pos))
def trigger_interrupt(self, type, data1=0, data2=0, data3=0):
pos = self.get_ih_rb_wptr()
self.physmem.write(self.ih_rb_base + pos, struct.pack(">IIII", type, data1, data2, data3))
self.set_ih_rb_wptr(pos + 16)
def trigger_vsync(self):
#avm.rpl seems to be signalling vsync on 'DVOCap' and 'TrigA' instead
#of the real vsync interrupt, we're triggering 'TrigA' here
if self.dc0.int_mask & 0x01000000: self.trigger_interrupt(2, 3)
if self.dc1.int_mask & 0x01000000: self.trigger_interrupt(6, 3)
def check_interrupts(self):
return self.get_ih_rb_wptr() != self.ih_rb_rptr
AI_CONTROL = 0xD006C00
AI_VOLUME = 0xD006C04
AI_AISCNT = 0xD006C08
AI_AIIT = 0xD006C0C
AI_START = 0xD006C00
AI_END = 0xD006C10
class AIController:
def __init__(self, scheduler):
self.scheduler = scheduler
self.control = 0
self.volume = 0
self.sample_count = 0
def read(self, addr):
if addr == AI_CONTROL: return self.control
elif addr == AI_VOLUME: return self.volume
elif addr == AI_AISCNT:
if self.control & 1:
self.sample_count += 1000
return self.sample_count
print("AI READ 0x%X at %08X" %(addr, self.scheduler.pc()))
return 0
def write(self, addr, value):
if addr == AI_CONTROL:
if value & 0x20:
self.sample_count = 0
self.control = value
elif addr == AI_VOLUME: self.volume = value
else:
print("AI WRITE 0x%X %08X at %08X" %(addr, value, self.scheduler.pc()))
DSP_MAILBOX_IN_H = 0xC280000
DSP_MAILBOX_IN_L = 0xC280002
DSP_MAILBOX_OUT_H = 0xC280004
DSP_MAILBOX_OUT_L = 0xC280006
DSP_CONTROL_STATUS = 0xC28000A
DSP_START = 0xC280000
DSP_END = 0xC2A0000
class DSPController:
STATE_NEXT = 0
STATE_ARG = 1
def __init__(self, scheduler):
self.scheduler = scheduler
self.mailbox_in = 0
self.mailbox_out = 0x80000000
self.dsp_int_status = 0
self.dsp_int_mask = 0
self.state = self.STATE_NEXT
self.message = 0
def read(self, addr):
print("DSP READ 0x%X at %08X" %(addr, self.scheduler.pc()))
if addr == DSP_MAILBOX_IN_H: return (self.mailbox_in >> 16) & 0x7FFF
elif addr == DSP_MAILBOX_OUT_H: return self.mailbox_out >> 16
elif addr == DSP_MAILBOX_OUT_L: return self.mailbox_out & 0xFFFF
elif addr == DSP_CONTROL_STATUS:
return (self.dsp_int_status << 7) | (self.dsp_int_mask << 8)
#print("DSP READ 0x%X at %08X" %(addr, self.scheduler.pc()))
return 0
def write(self, addr, value):
if addr == DSP_MAILBOX_IN_H: self.mailbox_in = (self.mailbox_in & 0xFFFF) | (value << 16)
elif addr == DSP_MAILBOX_IN_L:
self.mailbox_in = (self.mailbox_in & 0xFFFF0000) | value
self.handle_message(self.mailbox_in)
elif addr == DSP_CONTROL_STATUS:
if value & 0x80: self.dsp_int_status = 0
self.dsp_int_mask = (value >> 8) & 1
#else:
print("DSP WRITE 0x%X %08X at %08X" %(addr, value, self.scheduler.pc()))
def handle_message(self, value):
if self.state == self.STATE_NEXT:
self.message = value
self.state = self.STATE_ARG
else:
self.handle_request(self.message, value)
self.state = self.STATE_NEXT
def handle_request(self, message, arg):
if message == 0x80F3A001: pass #Task field_C (slave data addr)
elif message == 0x80F3A002: pass #Task field_10 (slave length)
elif message == 0x80F3B001: pass #Task field_18 (some kind of physical addr)
elif message == 0x80F3B002: pass #Task field_1C
elif message == 0x80F3C001: pass #Task field_20
elif message == 0x80F3C002: pass #Task field_14
elif message == 0x80F3D001: #Task field_24
self.trigger_dsp_interrupt(0xDCD10000)
else:
print("DSP MESSAGE %08X %08X at %08X" %(message, arg, self.scheduler.pc()))
def trigger_dsp_interrupt(self, message):
self.mailbox_out = message
self.dsp_int_status = 1
def check_interrupts(self):
return self.dsp_int_status and self.dsp_int_mask
TCL_D0000000 = 0xD0000000
class HardwareController:
def __init__(self, scheduler, physmem):
self.latte = LatteController(scheduler)
self.pad = PADController(scheduler)
self.gpu = GPUController(scheduler, physmem)
self.ai = AIController(scheduler)
self.dsp = DSPController(scheduler)
self.pi = [PIController(scheduler, self.latte.irq_ppc[i], self.gpu, self.dsp, i) for i in range(3)]
armirq = self.latte.irq_arm
self.ahmn = AHMNController(scheduler)
self.mem = MEMController(scheduler)
self.exi = EXIController(scheduler, armirq)
self.di2sata = DI2SATAController(scheduler)
self.ehci0 = EHCIController(scheduler, 0)
self.ehci1 = EHCIController(scheduler, 1)
self.ehci2 = EHCIController(scheduler, 2)
self.ohci00 = OHCIController(scheduler, armirq, physmem, 0)
self.ohci01 = OHCIController(scheduler, armirq, physmem, 1)
self.ohci1 = OHCIController(scheduler, armirq, physmem, 2)
self.ohci2 = OHCIController(scheduler, armirq, physmem, 3)
self.ahci = AHCIController(scheduler, armirq, physmem)
self.sdio0 = SDIOController(scheduler, armirq, physmem, 0, SDIOController.TYPE_UNK)
self.sdio1 = SDIOController(scheduler, armirq, physmem, 1, SDIOController.TYPE_SD)
self.sdio2 = SDIOController(scheduler, armirq, physmem, 2, SDIOController.TYPE_UNK)
self.wifi = SDIOController(scheduler, armirq, physmem, 3, SDIOController.TYPE_UNK)
self.nand = NANDController(scheduler, armirq, physmem)
self.aes = AESController(scheduler, armirq, physmem, 0)
self.aess = AESController(scheduler, armirq, physmem, 1)
self.sha = SHAController(scheduler, armirq, physmem, 0)
self.shas = SHAController(scheduler, armirq, physmem, 1)
self.scheduler = scheduler
def read(self, addr, length):
addr &= ~0x800000
if length == 2:
if MEM_START <= addr < MEM_END: value = self.mem.read(addr)
elif PAD_START <= addr < PAD_END: value = self.pad.read(addr)
elif DSP_START <= addr < DSP_END: value = self.dsp.read(addr)
else:
print("HW READ(2) 0x%X at %08X" %(addr, self.scheduler.pc()))
value = 0
return struct.pack(">H", value)
elif length == 4:
if LATTE_START <= addr < LATTE_END: value = self.latte.read(addr)
elif PI_CPU0_START <= addr < PI_CPU0_END: value = self.pi[0].read(addr - PI_CPU0_START)
elif PI_CPU1_START <= addr < PI_CPU1_END: value = self.pi[1].read(addr - PI_CPU1_START)
elif PI_CPU2_START <= addr < PI_CPU2_END: value = self.pi[2].read(addr - PI_CPU2_START)
elif GPU_START <= addr < GPU_END: value = self.gpu.read(addr)
elif AI_START <= addr < AI_END: value = self.ai.read(addr)
elif AHMN_START <= addr < AHMN_END: value = self.ahmn.read(addr)
elif EXI_START <= addr < EXI_END: value = self.exi.read(addr)
elif DI2SATA_START <= addr < DI2SATA_END: value = self.di2sata.read(addr)
elif EHCI0_START <= addr < EHCI0_END: value = self.ehci0.read(addr - EHCI0_START)
elif EHCI1_START <= addr < EHCI1_END: value = self.ehci1.read(addr - EHCI1_START)
elif EHCI2_START <= addr < EHCI2_END: value = self.ehci2.read(addr - EHCI2_START)
elif OHCI00_START <= addr < OHCI00_END: value = self.ohci00.read(addr - OHCI00_START)
elif OHCI01_START <= addr < OHCI01_END: value = self.ohci01.read(addr - OHCI01_START)
elif OHCI1_START <= addr < OHCI1_END: value = self.ohci1.read(addr - OHCI1_START)
elif OHCI2_START <= addr < OHCI2_END: value = self.ohci2.read(addr - OHCI2_START)
elif AHCI_START <= addr < AHCI_END: value = self.ahci.read(addr)
elif SDIO0_START <= addr < SDIO0_END: value = self.sdio0.read(addr - SDIO0_START)
elif SDIO1_START <= addr < SDIO1_END: value = self.sdio1.read(addr - SDIO1_START)
elif SDIO2_START <= addr < SDIO2_END: value = self.sdio2.read(addr - SDIO2_START)
elif WIFI_START <= addr < WIFI_END: value = self.wifi.read(addr - WIFI_START)
elif NAND_START <= addr < NAND_END: value = self.nand.read(addr)
elif AES_START <= addr < AES_END: value = self.aes.read(addr - AES_START)
elif AESS_START <= addr < AESS_END: value = self.aess.read(addr - AESS_START)
elif SHA_START <= addr < SHA_END: value = self.sha.read(addr - SHA_START)
elif SHAS_START <= addr < SHAS_END: value = self.shas.read(addr - SHAS_START)
elif addr == TCL_D0000000: value = 0
else:
print("HW READ(4) 0x%X at %08X" %(addr, self.scheduler.pc()))
value = 0
return struct.pack(">I", value)
else:
raise RuntimeError("Invalid HW read length: %i" %length)
def write(self, addr, data):
addr &= ~0x800000
if len(data) == 2:
value = struct.unpack(">H", data)[0]
if MEM_START <= addr < MEM_END: self.mem.write(addr, value)
elif PAD_START <= addr < PAD_END: self.pad.write(addr, value)
elif DSP_START <= addr < DSP_END: self.dsp.write(addr, value)
else:
print("HW WRITE 0x%X %04X at %08X" %(addr, value, self.scheduler.pc()))
elif len(data) == 4:
value = struct.unpack(">I", data)[0]
if LATTE_START <= addr < LATTE_END: self.latte.write(addr, value)
elif PI_CPU0_START <= addr < PI_CPU0_END: self.pi[0].write(addr - PI_CPU0_START, value)
elif PI_CPU1_START <= addr < PI_CPU1_END: self.pi[1].write(addr - PI_CPU1_START, value)
elif PI_CPU2_START <= addr < PI_CPU2_END: self.pi[2].write(addr - PI_CPU2_START, value)
elif GPU_START <= addr < GPU_END: self.gpu.write(addr, value)
elif AI_START <= addr < AI_END: self.ai.write(addr, value)
elif AHMN_START <= addr < AHMN_END: self.ahmn.write(addr, value)
elif MEM_START <= addr < MEM_END: self.mem.write(addr, value)
elif EXI_START <= addr < EXI_END: self.exi.write(addr, value)
elif DI2SATA_START <= addr < DI2SATA_END: self.di2sata.write(addr, value)
elif EHCI0_START <= addr < EHCI0_END: self.ehci0.write(addr - EHCI0_START, value)
elif EHCI1_START <= addr < EHCI1_END: self.ehci1.write(addr - EHCI1_START, value)
elif EHCI2_START <= addr < EHCI2_END: self.ehci2.write(addr - EHCI2_START, value)
elif OHCI00_START <= addr < OHCI00_END: self.ohci00.write(addr - OHCI00_START, value)
elif OHCI01_START <= addr < OHCI01_END: self.ohci01.write(addr - OHCI01_START, value)
elif OHCI1_START <= addr < OHCI1_END: self.ohci1.write(addr - OHCI1_START, value)
elif OHCI2_START <= addr < OHCI2_END: self.ohci2.write(addr - OHCI2_START, value)
elif AHCI_START <= addr < AHCI_END: self.ahci.write(addr, value)
elif SDIO0_START <= addr < SDIO0_END: self.sdio0.write(addr - SDIO0_START, value)
elif SDIO1_START <= addr < SDIO1_END: self.sdio1.write(addr - SDIO1_START, value)
elif SDIO2_START <= addr < SDIO2_END: self.sdio2.write(addr - SDIO2_START, value)
elif WIFI_START <= addr < WIFI_END: self.wifi.write(addr - WIFI_START, value)
elif NAND_START <= addr < NAND_END: self.nand.write(addr, value)
elif AES_START <= addr < AES_END: self.aes.write(addr - AES_START, value)
elif AESS_START <= addr < AESS_END: self.aess.write(addr - AESS_START, value)
elif SHA_START <= addr < SHA_END: self.sha.write(addr - SHA_START, value)
elif SHAS_START <= addr < SHAS_END: self.shas.write(addr - SHAS_START, value)
else:
print("HW WRITE 0x%X %08X at %08X" %(addr, value, self.scheduler.pc()))
else:
raise RuntimeError("Invalid HW write length: %i" %len(data))