From 1123e651d897e20385eeb4afed71ba964e4f161a Mon Sep 17 00:00:00 2001 From: Jannik Vogel Date: Sun, 22 Oct 2017 23:25:49 +0200 Subject: [PATCH 1/4] Rewrite get_timers.py using `Timer` class --- python-scripts/get_timers.py | 119 +++++++++++++++++++++++++++-------- 1 file changed, 94 insertions(+), 25 deletions(-) mode change 100644 => 100755 python-scripts/get_timers.py diff --git a/python-scripts/get_timers.py b/python-scripts/get_timers.py old mode 100644 new mode 100755 index 05ca9f8..a40b852 --- a/python-scripts/get_timers.py +++ b/python-scripts/get_timers.py @@ -17,40 +17,109 @@ NV2A_CRYSTAL_FREQ = 16666666 # Hz NV_PTIMER = 0x9000 +NV_PTIMER_NUMERATOR = 0x200 +NV_PTIMER_DENOMINATOR = 0x210 NV_PTIMER_TIME_0 = 0x400 NV_PTIMER_TIME_1 = 0x410 -NV_PTIMER_NUMERATOR = 0x00000200 -NV_PTIMER_DENOMINATOR = 0x00000210 -if __name__ == "__main__": +class Timer: + + def __init__(self, name): + self.name = name + self.base_ticks = 0 + self.last_ticks = None + self.last_update = None + + def Adjust(self): + self.base_ticks = self.GetTicks(False) + + def GetTicks(self, adjusted=True): + return self.ticks - (self.base_ticks if adjusted else 0) + + def GetFrequency(self): + return self.frequency + + def GetActualFrequency(self): + return self.actual_frequency + + def UpdateFrequency(self): + self.frequency = self.RetrieveFrequency() + + def UpdateTicks(self): + update_time = time.time() + self.ticks = self.RetrieveTicks() + if self.last_ticks is not None and self.last_update_time is not None: + self.actual_frequency = (self.ticks - self.last_ticks) / (update_time - self.last_update_time) + self.last_ticks = self.ticks + self.last_update_time = update_time + + def Print(self, flush=False): + ticks = self.GetTicks() + frequency = self.GetFrequency() + actual_frequency = self.GetActualFrequency() + #FIXME: Also show non-adjusted ticks somewhere? + print("%-14s %12d [f: %12.1f Hz] = %5.1f s [f: %12.1f Hz]" % (self.name + ":", ticks, frequency, ticks / frequency, actual_frequency), flush=flush) + + +class KeTickCountTimer(Timer): + + def __init__(self): + super(KeTickCountTimer, self).__init__("KeTickCount") + + def RetrieveFrequency(self): + return 1000.0 + + def RetrieveTicks(self): + return memory.read_u32(ke.KeTickCount()) - while(True): - KeTickCount = memory.read_u32(ke.KeTickCount()) - KeTickCountInSeconds = KeTickCount / 1000 +class GPUTimer(Timer): + def __init__(self): + super(GPUTimer, self).__init__("GPU Timer") + + def RetrieveFrequency(self): + self.GPUNumerator = nv2a.read_u32(NV_PTIMER + 0x200) + self.GPUDenominator = nv2a.read_u32(NV_PTIMER + 0x210) + + self.nvpll_coeff = nv2a.read_u32(NV_PRAMDAC + NV_PRAMDAC_NVPLL_COEFF) + self.mdiv = self.nvpll_coeff & NV_PRAMDAC_NVPLL_COEFF_MDIV + self.ndiv = (self.nvpll_coeff & NV_PRAMDAC_NVPLL_COEFF_NDIV) >> 8 + self.pdiv = (self.nvpll_coeff & NV_PRAMDAC_NVPLL_COEFF_PDIV) >> 16 + self.GPUClockrate = (NV2A_CRYSTAL_FREQ * self.ndiv) / (1 << self.pdiv) / self.mdiv + + return self.GPUClockrate / (self.GPUNumerator / self.GPUDenominator) + + def RetrieveTicks(self): GPUTimer0 = nv2a.read_u32(NV_PTIMER + NV_PTIMER_TIME_0) GPUTimer1 = nv2a.read_u32(NV_PTIMER + NV_PTIMER_TIME_1) GPUTimer0dec = (GPUTimer0 >> 5) & 0x7FFFFFF GPUTimer1dec = (GPUTimer1 & 0x1FFFFFFF) << 27 - GPUTimer = GPUTimer1dec | GPUTimer0dec - - GPUNumerator = nv2a.read_u32(NV_PTIMER + NV_PTIMER_NUMERATOR) - GPUDenominator = nv2a.read_u32(NV_PTIMER + NV_PTIMER_DENOMINATOR) - - nvpll_coeff = nv2a.read_u32(NV_PRAMDAC + NV_PRAMDAC_NVPLL_COEFF) - mdiv = nvpll_coeff & NV_PRAMDAC_NVPLL_COEFF_MDIV - ndiv = (nvpll_coeff & NV_PRAMDAC_NVPLL_COEFF_NDIV) >> 8 - pdiv = (nvpll_coeff & NV_PRAMDAC_NVPLL_COEFF_PDIV) >> 16 - GPUClockrate = (NV2A_CRYSTAL_FREQ * ndiv) / (1 << pdiv) / mdiv - GPUTimerClockrate = GPUClockrate / (GPUNumerator / GPUDenominator) - GPUTimerInSeconds = GPUTimer / GPUTimerClockrate - - print("GPU Clockrate: (NV2A_CRYSTAL_FREQ * ndiv) / (1 << pdiv) / mdiv") - print("GPU Clockrate: (" + str(NV2A_CRYSTAL_FREQ)+" * " + str(ndiv) + ") / (1 << " + str(pdiv) + ") / " + str(mdiv) + " = " + str(GPUClockrate) + " Hz") - print("GPU Timer clockrate: " + str(GPUClockrate) + " / (" + str(GPUNumerator) + " / " + str(GPUDenominator) + ") = " + str(GPUTimerClockrate) + " Hz") - print("GPU Timer: " + str(GPUTimer) + " [" + str(round(GPUTimerInSeconds)) + " s]") - print("KeTickCount: " + str(KeTickCount) + " [" + str(round(KeTickCountInSeconds)) + " s]") - print() + return GPUTimer1dec | GPUTimer0dec + def Print(self): + super().Print() + print(" Core clockrate: ( XTAL * n) / (1 << p) / m", flush=False) + print(" (%-8d * %3d) / (1 << %3d) / %3d = %.1f Hz" % (NV2A_CRYSTAL_FREQ, self.ndiv, self.pdiv, self.mdiv, self.GPUClockrate), flush=False) + print(" Timer clockrate: %.1f / (%d / %d)" % (self.GPUClockrate, self.GPUNumerator, self.GPUDenominator), flush=False) + + +if __name__ == "__main__": + + timers = [] + timers.append(KeTickCountTimer()) + timers.append(GPUTimer()) + + for timer in timers: + timer.UpdateFrequency() + timer.UpdateTicks() + timer.Adjust() #FIXME: Make this optional + + while(True): + + for timer in timers: + timer.UpdateTicks() + timer.Print() + + print(flush=True) time.sleep(0.02) From 2d342a78f7d56656be8d4b124ac5c3ba6122ca09 Mon Sep 17 00:00:00 2001 From: Jannik Vogel Date: Tue, 31 Oct 2017 00:34:02 +0100 Subject: [PATCH 2/4] Add RDTSC Timer --- python-scripts/get_timers.py | 26 ++++++++++++++++++++++++++ 1 file changed, 26 insertions(+) diff --git a/python-scripts/get_timers.py b/python-scripts/get_timers.py index a40b852..703a13b 100755 --- a/python-scripts/get_timers.py +++ b/python-scripts/get_timers.py @@ -5,6 +5,7 @@ from xbox import * import time +import struct NV_PRAMDAC = 0x680000 NV_PRAMDAC_NVPLL_COEFF = 0x500 @@ -72,6 +73,29 @@ def RetrieveFrequency(self): def RetrieveTicks(self): return memory.read_u32(ke.KeTickCount()) +class RDTSCTimer(Timer): + + def __init__(self): + super(RDTSCTimer, self).__init__("RDTSC") + code = bytes([ + 0x0F, 0x31, # rdtsc + 0x8B, 0x4C, 0x24, 0x04, # mov ecx,DWORD PTR [esp+0x4] + 0x89, 0x01, # mov DWORD PTR [ecx],eax + 0x89, 0x51, 0x04, # mov DWORD PTR [ecx+0x4],edx + 0xC2, 0x04, 0x00 # ret 0x4 + ]) + pointer = ke.MmAllocateContiguousMemory(len(code) + 8) + memory.write(pointer, code) + self.code = pointer + self.data = pointer + len(code) + + def RetrieveFrequency(self): + return 2200000000.0 / 3.0 # 733.3 MHz + + def RetrieveTicks(self): + api.call(self.code, struct.pack(" Date: Mon, 2 Jul 2018 20:52:48 +0200 Subject: [PATCH 3/4] Print all timers at once --- python-scripts/dsp_homebrew.py | 85 +++++++++++++----------- python-scripts/get_timers.py | 24 +++++-- python-scripts/xbox/interface/if_xbdm.py | 2 +- 3 files changed, 67 insertions(+), 44 deletions(-) diff --git a/python-scripts/dsp_homebrew.py b/python-scripts/dsp_homebrew.py index 02f0f6d..32e56d8 100755 --- a/python-scripts/dsp_homebrew.py +++ b/python-scripts/dsp_homebrew.py @@ -3,7 +3,7 @@ # Run this from a clean Xbox environment (= not while a game is running) # NXDK-RDT is a good environment -from xbox import * +from xboxpy.xboxpy import * import sys import time @@ -12,24 +12,24 @@ def dsp_homebrew(): #FIXME: Pass dsp object which provides all the device details and registers instead # Disable DSP - apu.write_u32(NV_PAPU_EPRST, NV_PAPU_EPRST_EPRST) # If this is zero, the EP will not allow reads/writes to memory?! + apu.write_u32(NV_PAPU_GPRST, NV_PAPU_GPRST_GPRST) # If this is zero, the GP will not allow reads/writes to memory?! time.sleep(0.1) # FIXME: Not sure if DSP reset is synchronous, so we wait for now # Allocate some scratch space (at least 2 pages!) #FIXME: Free memory after running page_count = 2 page_head = ke.MmAllocateContiguousMemory(4096) - apu.write_u32(NV_PAPU_EPSADDR, ke.MmGetPhysicalAddress(page_head)) + apu.write_u32(NV_PAPU_GPSADDR, ke.MmGetPhysicalAddress(page_head)) page_base = ke.MmAllocateContiguousMemory(4096 * page_count) for i in range(0, page_count): write_u32(page_head + i * 8 + 0, ke.MmGetPhysicalAddress(page_base + 0x1000 * i)) write_u32(page_head + i * 8 + 4, 0) # Control - apu.write_u32(NV_PAPU_EPSMAXSGE, page_count - 1) + apu.write_u32(NV_PAPU_GPSMAXSGE, page_count - 1) # It was assembled using `a56 loop.inc && toomf < a56.out`. # The resulting code was then copied here. # `a56` (inc. `toomf`) can be found at: http://www.zdomain.com/a56.html - if True: + if False: print("Starting assembler") #raise #FIXME: Test this codepath data = dsp.assemble(""" @@ -47,50 +47,61 @@ def dsp_homebrew(): for i in range(0, len(code_words)): data += int.to_bytes(int(code_words[i], 16), length=3, byteorder='little', signed=False) - code = open("tiny-gp.inc").read() - print(code) - data = dsp.assemble(code) + if False: + code = open("tiny-gp.inc").read() + print(code) + data = dsp.assemble(code) + + print(data) # Convert the 24 bit words to 32 bit words data = dsp.from24(data) - # Write code to PMEM (normally you can just use write() but we don't support that for apu MMIO yet.. boo!) - for i in range(0, len(data) // 4): - word = int.from_bytes(data[i*4:i*4+4], byteorder='little', signed=False) & 0xFFFFFF - apu.write_u32(NV_PAPU_EPPMEM + i*4, word) - # According to XQEMU, 0x800 * 4 bytes will be loaded from scratch to PMEM at startup. - # So just to be sure, let's also write this to the scratch.. - write_u32(page_base + i*4, word) + while True: + + # Write code to PMEM (normally you can just use write() but we don't support that for apu MMIO yet.. boo!) + for i in range(0, len(data) // 4): + word = int.from_bytes(data[i*4:i*4+4], byteorder='little', signed=False) & 0xFFFFFF + apu.write_u32(NV_PAPU_GPPMEM + i*4, word) + # According to XQEMU, 0x800 * 4 bytes will be loaded from scratch to PMEM at startup. + # So just to be sure, let's also write this to the scratch.. + write_u32(page_base + i*4, word) + + # Set XMEM + apu.write_u32(NV_PAPU_GPXMEM + 0*4, 0x001337) + + # Set YMEM + apu.write_u32(NV_PAPU_GPYMEM + 0*4, 0x000000) - # Set XMEM - apu.write_u32(NV_PAPU_EPXMEM + 0*4, 0x001337) + # Test readback + print("Read back X[0]:0x" + format(apu.read_u32(NV_PAPU_GPXMEM + 0*4), '06X')) + print("Read back Y[0]:0x" + format(apu.read_u32(NV_PAPU_GPYMEM + 0*4), '06X')) - # Set YMEM - apu.write_u32(NV_PAPU_EPYMEM + 0*4, 0x000000) + print("Read back P[0]:0x" + format(apu.read_u32(NV_PAPU_GPPMEM + 0*4), '06X')) + print("Read back P[1]:0x" + format(apu.read_u32(NV_PAPU_GPPMEM + 1*4), '06X')) - # Test readback - print("Read back X[0]:0x" + format(apu.read_u32(NV_PAPU_EPXMEM + 0*4), '06X')) - print("Read back Y[0]:0x" + format(apu.read_u32(NV_PAPU_EPYMEM + 0*4), '06X')) + # Set frame duration (?!) + apu.write_u32(NV_PAPU_SECTL, 3 << 3) - print("Read back P[0]:0x" + format(apu.read_u32(NV_PAPU_EPPMEM + 0*4), '06X')) - print("Read back P[1]:0x" + format(apu.read_u32(NV_PAPU_EPPMEM + 1*4), '06X')) + # Enable DSP + # NV_PAPU_GPRST_GPRST < crashes! + # NV_PAPU_GPRST_GPDSPRST < works! + apu.write_u32(NV_PAPU_GPRST, NV_PAPU_GPRST_GPRST | NV_PAPU_GPRST_GPDSPRST) + time.sleep(0.1) - # Set frame duration (?!) - apu.write_u32(NV_PAPU_SECTL, 3 << 3) + # Write X again. Bootcode in the DSP seems to overwrites XMEM + YMEM + apu.write_u32(NV_PAPU_GPXMEM + 0*4, 0x001338) - # Enable DSP - # NV_PAPU_EPRST_EPRST < crashes! - # NV_PAPU_EPRST_EPDSPRST < works! - apu.write_u32(NV_PAPU_EPRST, NV_PAPU_EPRST_EPRST | NV_PAPU_EPRST_EPDSPRST) - time.sleep(0.1) + time.sleep(0.5) - # Write X again. Bootcode in the DSP seems to overwrites XMEM + YMEM - apu.write_u32(NV_PAPU_EPXMEM + 0*4, 0x001337) + #if (apu.read_u32(NV_PAPU_GPXMEM + 0*4) == 0x1338): + # continue - time.sleep(0.1) + # Read destination data from YMEM + #while True: + print("Read back X[0]:0x" + format(apu.read_u32(NV_PAPU_GPXMEM + 0*4), '06X')) + print("Read back Y[0]:0x" + format(apu.read_u32(NV_PAPU_GPYMEM + 0*4), '06X')) + time.sleep(0.5) - # Read destination data from YMEM - print("Read back X[0]:0x" + format(apu.read_u32(NV_PAPU_EPXMEM + 0*4), '06X')) - print("Read back Y[0]:0x" + format(apu.read_u32(NV_PAPU_EPYMEM + 0*4), '06X')) dsp_homebrew() diff --git a/python-scripts/get_timers.py b/python-scripts/get_timers.py index 703a13b..f09b37c 100755 --- a/python-scripts/get_timers.py +++ b/python-scripts/get_timers.py @@ -4,6 +4,10 @@ from xbox import * + + +from io import StringIO +import sys import time import struct @@ -54,12 +58,12 @@ def UpdateTicks(self): self.last_ticks = self.ticks self.last_update_time = update_time - def Print(self, flush=False): + def Print(self): ticks = self.GetTicks() frequency = self.GetFrequency() actual_frequency = self.GetActualFrequency() #FIXME: Also show non-adjusted ticks somewhere? - print("%-14s %12d [f: %12.1f Hz] = %5.1f s [f: %12.1f Hz]" % (self.name + ":", ticks, frequency, ticks / frequency, actual_frequency), flush=flush) + print("%-14s %12d [f: %12.1f Hz] = %5.1f s [f: %12.1f Hz]" % (self.name + ":", ticks, frequency, ticks / frequency, actual_frequency)) class KeTickCountTimer(Timer): @@ -123,9 +127,9 @@ def RetrieveTicks(self): def Print(self): super().Print() - print(" Core clockrate: ( XTAL * n) / (1 << p) / m", flush=False) - print(" (%-8d * %3d) / (1 << %3d) / %3d = %.1f Hz" % (NV2A_CRYSTAL_FREQ, self.ndiv, self.pdiv, self.mdiv, self.GPUClockrate), flush=False) - print(" Timer clockrate: %.1f / (%d / %d)" % (self.GPUClockrate, self.GPUNumerator, self.GPUDenominator), flush=False) + print(" Core clockrate: ( XTAL * n) / (1 << p) / m") + print(" (%-8d * %3d) / (1 << %3d) / %3d = %.1f Hz" % (NV2A_CRYSTAL_FREQ, self.ndiv, self.pdiv, self.mdiv, self.GPUClockrate)) + print(" Timer clockrate: %.1f / (%d / %d)" % (self.GPUClockrate, self.GPUNumerator, self.GPUDenominator)) if __name__ == "__main__": @@ -143,9 +147,17 @@ def Print(self): while(True): + # Setup a temporary stdout, so we can buffer the print for all timers + real_stdout = sys.stdout + sys.stdout = buffer_stdout = StringIO() + + # Update and print each timer for timer in timers: timer.UpdateTicks() timer.Print() - print(flush=True) + # Switch to the real stdout again to output all timers at once + sys.stdout = real_stdout + print(buffer_stdout.getvalue(), flush=True) + time.sleep(0.02) diff --git a/python-scripts/xbox/interface/if_xbdm.py b/python-scripts/xbox/interface/if_xbdm.py index ed4f7c0..81149c4 100644 --- a/python-scripts/xbox/interface/if_xbdm.py +++ b/python-scripts/xbox/interface/if_xbdm.py @@ -197,7 +197,7 @@ def write1(address, data, physical): xbdm_base = xbdm_module['base'] DmResumeThread_addr = resolve_export(35, image_base=xbdm_base) - hack = "0F20C05025FFFFFEFF0F22C08B5424088B1A8B4A048B4208E2028A03E203668B03E2028B03E2028803E203668903E2028903894208580F22C0B80000DB02C20400" + hack = "0F20C05025FFFFFEFF0F22C08B5424088B1A8B4A048B4208E2028A03E203668B03E2028B03E2028803E203668903E2028903E2126089C141497408FF720C83C204EBF5FFD361894208580F22C0B80000DB02C20400" xbdm_command("setmem addr=0x" + format(DmResumeThread_addr, 'X') + " data=" + hack) #hack_bank = DmResumeThread_addr + (len(hack) // 2) # Put communication base behind the hack code [pretty shitty..] From 85e04564a5c856162383afc7103baa3459705d1d Mon Sep 17 00:00:00 2001 From: Jannik Vogel Date: Mon, 2 Jul 2018 20:56:22 +0200 Subject: [PATCH 4/4] Use xboxpy for get_timers.py --- .gitmodules | 3 +++ python-scripts/README.md | 15 ++++++++++++++- python-scripts/get_timers.py | 2 +- python-scripts/xboxpy | 1 + 4 files changed, 19 insertions(+), 2 deletions(-) create mode 100644 .gitmodules create mode 160000 python-scripts/xboxpy diff --git a/.gitmodules b/.gitmodules new file mode 100644 index 0000000..5fca768 --- /dev/null +++ b/.gitmodules @@ -0,0 +1,3 @@ +[submodule "python-scripts/xboxpy"] + path = python-scripts/xboxpy + url = https://github.com/XboxDev/xboxpy.git diff --git a/python-scripts/README.md b/python-scripts/README.md index 10b4da1..1b48374 100644 --- a/python-scripts/README.md +++ b/python-scripts/README.md @@ -2,7 +2,20 @@ This is a collection of scripts to access various Xbox hardware. -## Usage + +## Usage for updated scripts + +Some code might be using [xboxpy](https://github.com/XboxDev/xboxpy) already. +Please check the respective xboxpy documentation. + +Also initialize the submodules to be able use those scripts: + +``` +git submodule update --init --recursive +``` + + +## Usage for legacy scripts * All stuff is internally imported by the `xbox` module. So: `import xbox` * You can define the interface you want to use using environment variable 'XBOX_IF': diff --git a/python-scripts/get_timers.py b/python-scripts/get_timers.py index f09b37c..942e770 100755 --- a/python-scripts/get_timers.py +++ b/python-scripts/get_timers.py @@ -2,7 +2,7 @@ # Prints information about some of the Xbox timers -from xbox import * +from xboxpy.xboxpy import * diff --git a/python-scripts/xboxpy b/python-scripts/xboxpy new file mode 160000 index 0000000..81ba032 --- /dev/null +++ b/python-scripts/xboxpy @@ -0,0 +1 @@ +Subproject commit 81ba03297d5e4aa6d3be500b6b9c180f03343ca0