Skip to content

Commit

Permalink
Merge pull request #50 from JayFoxRox/get_timer-classes
Browse files Browse the repository at this point in the history
Rewrite get_timers.py using `Timer` class
  • Loading branch information
JayFoxRox committed Jul 2, 2018
2 parents 83d5053 + 85e0456 commit 2d2169d
Show file tree
Hide file tree
Showing 6 changed files with 200 additions and 65 deletions.
3 changes: 3 additions & 0 deletions .gitmodules
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
[submodule "python-scripts/xboxpy"]
path = python-scripts/xboxpy
url = https://github.com/XboxDev/xboxpy.git
15 changes: 14 additions & 1 deletion python-scripts/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -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':
Expand Down
85 changes: 48 additions & 37 deletions python-scripts/dsp_homebrew.py
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand All @@ -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("""
Expand All @@ -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()
159 changes: 133 additions & 26 deletions python-scripts/get_timers.py
100644 → 100755
Original file line number Diff line number Diff line change
Expand Up @@ -2,9 +2,14 @@

# Prints information about some of the Xbox timers

from xbox import *
from xboxpy.xboxpy import *



from io import StringIO
import sys
import time
import struct

NV_PRAMDAC = 0x680000
NV_PRAMDAC_NVPLL_COEFF = 0x500
Expand All @@ -17,40 +22,142 @@
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:

while(True):
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):
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))


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())

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

KeTickCount = memory.read_u32(ke.KeTickCount())
KeTickCountInSeconds = KeTickCount / 1000
def RetrieveTicks(self):
api.call(self.code, struct.pack("<I", self.data))
return (memory.read_u32(self.data + 4) << 32) | memory.read_u32(self.data + 0)


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")
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__":

timers = []

timers.append(RDTSCTimer())
timers.append(KeTickCountTimer())
timers.append(GPUTimer())

for timer in timers:
timer.UpdateFrequency()
timer.UpdateTicks()
timer.Adjust() #FIXME: Make this optional

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()

# 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)
2 changes: 1 addition & 1 deletion python-scripts/xbox/interface/if_xbdm.py
Original file line number Diff line number Diff line change
Expand Up @@ -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..]
Expand Down
1 change: 1 addition & 0 deletions python-scripts/xboxpy
Submodule xboxpy added at 81ba03

0 comments on commit 2d2169d

Please sign in to comment.