├── .gitignore ├── LICENSE ├── README.md ├── setup.py └── xboxpy ├── __init__.py ├── aci.py ├── apu.py ├── apu_regs.py ├── dsp.py ├── helper ├── __init__.py ├── better_wave.py └── fields.py ├── interface ├── __init__.py ├── api.py ├── dbg_pb2.py ├── if_gdb.py ├── if_nxdk_rdt.py ├── if_xbdm.py └── xbdm-hack.md ├── ke.py ├── memory.py ├── nv2a.py ├── ohci.py ├── ohci_pad.py └── pe.py /.gitignore: -------------------------------------------------------------------------------- 1 | # Cache 2 | __pycache__/ 3 | 4 | # Distribution / packaging 5 | *.egg-info/ 6 | dist/ 7 | build/ 8 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | GNU GENERAL PUBLIC LICENSE 2 | Version 2, June 1991 3 | 4 | Copyright (C) 1989, 1991 Free Software Foundation, Inc., 5 | 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA 6 | Everyone is permitted to copy and distribute verbatim copies 7 | of this license document, but changing it is not allowed. 8 | 9 | Preamble 10 | 11 | The licenses for most software are designed to take away your 12 | freedom to share and change it. By contrast, the GNU General Public 13 | License is intended to guarantee your freedom to share and change free 14 | software--to make sure the software is free for all its users. This 15 | General Public License applies to most of the Free Software 16 | Foundation's software and to any other program whose authors commit to 17 | using it. (Some other Free Software Foundation software is covered by 18 | the GNU Lesser General Public License instead.) You can apply it to 19 | your programs, too. 20 | 21 | When we speak of free software, we are referring to freedom, not 22 | price. Our General Public Licenses are designed to make sure that you 23 | have the freedom to distribute copies of free software (and charge for 24 | this service if you wish), that you receive source code or can get it 25 | if you want it, that you can change the software or use pieces of it 26 | in new free programs; and that you know you can do these things. 27 | 28 | To protect your rights, we need to make restrictions that forbid 29 | anyone to deny you these rights or to ask you to surrender the rights. 30 | These restrictions translate to certain responsibilities for you if you 31 | distribute copies of the software, or if you modify it. 32 | 33 | For example, if you distribute copies of such a program, whether 34 | gratis or for a fee, you must give the recipients all the rights that 35 | you have. You must make sure that they, too, receive or can get the 36 | source code. And you must show them these terms so they know their 37 | rights. 38 | 39 | We protect your rights with two steps: (1) copyright the software, and 40 | (2) offer you this license which gives you legal permission to copy, 41 | distribute and/or modify the software. 42 | 43 | Also, for each author's protection and ours, we want to make certain 44 | that everyone understands that there is no warranty for this free 45 | software. If the software is modified by someone else and passed on, we 46 | want its recipients to know that what they have is not the original, so 47 | that any problems introduced by others will not reflect on the original 48 | authors' reputations. 49 | 50 | Finally, any free program is threatened constantly by software 51 | patents. We wish to avoid the danger that redistributors of a free 52 | program will individually obtain patent licenses, in effect making the 53 | program proprietary. To prevent this, we have made it clear that any 54 | patent must be licensed for everyone's free use or not licensed at all. 55 | 56 | The precise terms and conditions for copying, distribution and 57 | modification follow. 58 | 59 | GNU GENERAL PUBLIC LICENSE 60 | TERMS AND CONDITIONS FOR COPYING, DISTRIBUTION AND MODIFICATION 61 | 62 | 0. This License applies to any program or other work which contains 63 | a notice placed by the copyright holder saying it may be distributed 64 | under the terms of this General Public License. The "Program", below, 65 | refers to any such program or work, and a "work based on the Program" 66 | means either the Program or any derivative work under copyright law: 67 | that is to say, a work containing the Program or a portion of it, 68 | either verbatim or with modifications and/or translated into another 69 | language. (Hereinafter, translation is included without limitation in 70 | the term "modification".) Each licensee is addressed as "you". 71 | 72 | Activities other than copying, distribution and modification are not 73 | covered by this License; they are outside its scope. The act of 74 | running the Program is not restricted, and the output from the Program 75 | is covered only if its contents constitute a work based on the 76 | Program (independent of having been made by running the Program). 77 | Whether that is true depends on what the Program does. 78 | 79 | 1. You may copy and distribute verbatim copies of the Program's 80 | source code as you receive it, in any medium, provided that you 81 | conspicuously and appropriately publish on each copy an appropriate 82 | copyright notice and disclaimer of warranty; keep intact all the 83 | notices that refer to this License and to the absence of any warranty; 84 | and give any other recipients of the Program a copy of this License 85 | along with the Program. 86 | 87 | You may charge a fee for the physical act of transferring a copy, and 88 | you may at your option offer warranty protection in exchange for a fee. 89 | 90 | 2. You may modify your copy or copies of the Program or any portion 91 | of it, thus forming a work based on the Program, and copy and 92 | distribute such modifications or work under the terms of Section 1 93 | above, provided that you also meet all of these conditions: 94 | 95 | a) You must cause the modified files to carry prominent notices 96 | stating that you changed the files and the date of any change. 97 | 98 | b) You must cause any work that you distribute or publish, that in 99 | whole or in part contains or is derived from the Program or any 100 | part thereof, to be licensed as a whole at no charge to all third 101 | parties under the terms of this License. 102 | 103 | c) If the modified program normally reads commands interactively 104 | when run, you must cause it, when started running for such 105 | interactive use in the most ordinary way, to print or display an 106 | announcement including an appropriate copyright notice and a 107 | notice that there is no warranty (or else, saying that you provide 108 | a warranty) and that users may redistribute the program under 109 | these conditions, and telling the user how to view a copy of this 110 | License. (Exception: if the Program itself is interactive but 111 | does not normally print such an announcement, your work based on 112 | the Program is not required to print an announcement.) 113 | 114 | These requirements apply to the modified work as a whole. If 115 | identifiable sections of that work are not derived from the Program, 116 | and can be reasonably considered independent and separate works in 117 | themselves, then this License, and its terms, do not apply to those 118 | sections when you distribute them as separate works. But when you 119 | distribute the same sections as part of a whole which is a work based 120 | on the Program, the distribution of the whole must be on the terms of 121 | this License, whose permissions for other licensees extend to the 122 | entire whole, and thus to each and every part regardless of who wrote it. 123 | 124 | Thus, it is not the intent of this section to claim rights or contest 125 | your rights to work written entirely by you; rather, the intent is to 126 | exercise the right to control the distribution of derivative or 127 | collective works based on the Program. 128 | 129 | In addition, mere aggregation of another work not based on the Program 130 | with the Program (or with a work based on the Program) on a volume of 131 | a storage or distribution medium does not bring the other work under 132 | the scope of this License. 133 | 134 | 3. You may copy and distribute the Program (or a work based on it, 135 | under Section 2) in object code or executable form under the terms of 136 | Sections 1 and 2 above provided that you also do one of the following: 137 | 138 | a) Accompany it with the complete corresponding machine-readable 139 | source code, which must be distributed under the terms of Sections 140 | 1 and 2 above on a medium customarily used for software interchange; or, 141 | 142 | b) Accompany it with a written offer, valid for at least three 143 | years, to give any third party, for a charge no more than your 144 | cost of physically performing source distribution, a complete 145 | machine-readable copy of the corresponding source code, to be 146 | distributed under the terms of Sections 1 and 2 above on a medium 147 | customarily used for software interchange; or, 148 | 149 | c) Accompany it with the information you received as to the offer 150 | to distribute corresponding source code. (This alternative is 151 | allowed only for noncommercial distribution and only if you 152 | received the program in object code or executable form with such 153 | an offer, in accord with Subsection b above.) 154 | 155 | The source code for a work means the preferred form of the work for 156 | making modifications to it. For an executable work, complete source 157 | code means all the source code for all modules it contains, plus any 158 | associated interface definition files, plus the scripts used to 159 | control compilation and installation of the executable. However, as a 160 | special exception, the source code distributed need not include 161 | anything that is normally distributed (in either source or binary 162 | form) with the major components (compiler, kernel, and so on) of the 163 | operating system on which the executable runs, unless that component 164 | itself accompanies the executable. 165 | 166 | If distribution of executable or object code is made by offering 167 | access to copy from a designated place, then offering equivalent 168 | access to copy the source code from the same place counts as 169 | distribution of the source code, even though third parties are not 170 | compelled to copy the source along with the object code. 171 | 172 | 4. You may not copy, modify, sublicense, or distribute the Program 173 | except as expressly provided under this License. Any attempt 174 | otherwise to copy, modify, sublicense or distribute the Program is 175 | void, and will automatically terminate your rights under this License. 176 | However, parties who have received copies, or rights, from you under 177 | this License will not have their licenses terminated so long as such 178 | parties remain in full compliance. 179 | 180 | 5. You are not required to accept this License, since you have not 181 | signed it. However, nothing else grants you permission to modify or 182 | distribute the Program or its derivative works. These actions are 183 | prohibited by law if you do not accept this License. Therefore, by 184 | modifying or distributing the Program (or any work based on the 185 | Program), you indicate your acceptance of this License to do so, and 186 | all its terms and conditions for copying, distributing or modifying 187 | the Program or works based on it. 188 | 189 | 6. Each time you redistribute the Program (or any work based on the 190 | Program), the recipient automatically receives a license from the 191 | original licensor to copy, distribute or modify the Program subject to 192 | these terms and conditions. You may not impose any further 193 | restrictions on the recipients' exercise of the rights granted herein. 194 | You are not responsible for enforcing compliance by third parties to 195 | this License. 196 | 197 | 7. If, as a consequence of a court judgment or allegation of patent 198 | infringement or for any other reason (not limited to patent issues), 199 | conditions are imposed on you (whether by court order, agreement or 200 | otherwise) that contradict the conditions of this License, they do not 201 | excuse you from the conditions of this License. If you cannot 202 | distribute so as to satisfy simultaneously your obligations under this 203 | License and any other pertinent obligations, then as a consequence you 204 | may not distribute the Program at all. For example, if a patent 205 | license would not permit royalty-free redistribution of the Program by 206 | all those who receive copies directly or indirectly through you, then 207 | the only way you could satisfy both it and this License would be to 208 | refrain entirely from distribution of the Program. 209 | 210 | If any portion of this section is held invalid or unenforceable under 211 | any particular circumstance, the balance of the section is intended to 212 | apply and the section as a whole is intended to apply in other 213 | circumstances. 214 | 215 | It is not the purpose of this section to induce you to infringe any 216 | patents or other property right claims or to contest validity of any 217 | such claims; this section has the sole purpose of protecting the 218 | integrity of the free software distribution system, which is 219 | implemented by public license practices. Many people have made 220 | generous contributions to the wide range of software distributed 221 | through that system in reliance on consistent application of that 222 | system; it is up to the author/donor to decide if he or she is willing 223 | to distribute software through any other system and a licensee cannot 224 | impose that choice. 225 | 226 | This section is intended to make thoroughly clear what is believed to 227 | be a consequence of the rest of this License. 228 | 229 | 8. If the distribution and/or use of the Program is restricted in 230 | certain countries either by patents or by copyrighted interfaces, the 231 | original copyright holder who places the Program under this License 232 | may add an explicit geographical distribution limitation excluding 233 | those countries, so that distribution is permitted only in or among 234 | countries not thus excluded. In such case, this License incorporates 235 | the limitation as if written in the body of this License. 236 | 237 | 9. The Free Software Foundation may publish revised and/or new versions 238 | of the General Public License from time to time. Such new versions will 239 | be similar in spirit to the present version, but may differ in detail to 240 | address new problems or concerns. 241 | 242 | Each version is given a distinguishing version number. If the Program 243 | specifies a version number of this License which applies to it and "any 244 | later version", you have the option of following the terms and conditions 245 | either of that version or of any later version published by the Free 246 | Software Foundation. If the Program does not specify a version number of 247 | this License, you may choose any version ever published by the Free Software 248 | Foundation. 249 | 250 | 10. If you wish to incorporate parts of the Program into other free 251 | programs whose distribution conditions are different, write to the author 252 | to ask for permission. For software which is copyrighted by the Free 253 | Software Foundation, write to the Free Software Foundation; we sometimes 254 | make exceptions for this. Our decision will be guided by the two goals 255 | of preserving the free status of all derivatives of our free software and 256 | of promoting the sharing and reuse of software generally. 257 | 258 | NO WARRANTY 259 | 260 | 11. BECAUSE THE PROGRAM IS LICENSED FREE OF CHARGE, THERE IS NO WARRANTY 261 | FOR THE PROGRAM, TO THE EXTENT PERMITTED BY APPLICABLE LAW. EXCEPT WHEN 262 | OTHERWISE STATED IN WRITING THE COPYRIGHT HOLDERS AND/OR OTHER PARTIES 263 | PROVIDE THE PROGRAM "AS IS" WITHOUT WARRANTY OF ANY KIND, EITHER EXPRESSED 264 | OR IMPLIED, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF 265 | MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE. THE ENTIRE RISK AS 266 | TO THE QUALITY AND PERFORMANCE OF THE PROGRAM IS WITH YOU. SHOULD THE 267 | PROGRAM PROVE DEFECTIVE, YOU ASSUME THE COST OF ALL NECESSARY SERVICING, 268 | REPAIR OR CORRECTION. 269 | 270 | 12. IN NO EVENT UNLESS REQUIRED BY APPLICABLE LAW OR AGREED TO IN WRITING 271 | WILL ANY COPYRIGHT HOLDER, OR ANY OTHER PARTY WHO MAY MODIFY AND/OR 272 | REDISTRIBUTE THE PROGRAM AS PERMITTED ABOVE, BE LIABLE TO YOU FOR DAMAGES, 273 | INCLUDING ANY GENERAL, SPECIAL, INCIDENTAL OR CONSEQUENTIAL DAMAGES ARISING 274 | OUT OF THE USE OR INABILITY TO USE THE PROGRAM (INCLUDING BUT NOT LIMITED 275 | TO LOSS OF DATA OR DATA BEING RENDERED INACCURATE OR LOSSES SUSTAINED BY 276 | YOU OR THIRD PARTIES OR A FAILURE OF THE PROGRAM TO OPERATE WITH ANY OTHER 277 | PROGRAMS), EVEN IF SUCH HOLDER OR OTHER PARTY HAS BEEN ADVISED OF THE 278 | POSSIBILITY OF SUCH DAMAGES. 279 | 280 | END OF TERMS AND CONDITIONS 281 | 282 | How to Apply These Terms to Your New Programs 283 | 284 | If you develop a new program, and you want it to be of the greatest 285 | possible use to the public, the best way to achieve this is to make it 286 | free software which everyone can redistribute and change under these terms. 287 | 288 | To do so, attach the following notices to the program. It is safest 289 | to attach them to the start of each source file to most effectively 290 | convey the exclusion of warranty; and each file should have at least 291 | the "copyright" line and a pointer to where the full notice is found. 292 | 293 | 294 | Copyright (C) 295 | 296 | This program is free software; you can redistribute it and/or modify 297 | it under the terms of the GNU General Public License as published by 298 | the Free Software Foundation; either version 2 of the License, or 299 | (at your option) any later version. 300 | 301 | This program is distributed in the hope that it will be useful, 302 | but WITHOUT ANY WARRANTY; without even the implied warranty of 303 | MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 304 | GNU General Public License for more details. 305 | 306 | You should have received a copy of the GNU General Public License along 307 | with this program; if not, write to the Free Software Foundation, Inc., 308 | 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. 309 | 310 | Also add information on how to contact you by electronic and paper mail. 311 | 312 | If the program is interactive, make it output a short notice like this 313 | when it starts in an interactive mode: 314 | 315 | Gnomovision version 69, Copyright (C) year name of author 316 | Gnomovision comes with ABSOLUTELY NO WARRANTY; for details type `show w'. 317 | This is free software, and you are welcome to redistribute it 318 | under certain conditions; type `show c' for details. 319 | 320 | The hypothetical commands `show w' and `show c' should show the appropriate 321 | parts of the General Public License. Of course, the commands you use may 322 | be called something other than `show w' and `show c'; they could even be 323 | mouse-clicks or menu items--whatever suits your program. 324 | 325 | You should also get your employer (if you work as a programmer) or your 326 | school, if any, to sign a "copyright disclaimer" for the program, if 327 | necessary. Here is a sample; alter the names: 328 | 329 | Yoyodyne, Inc., hereby disclaims all copyright interest in the program 330 | `Gnomovision' (which makes passes at compilers) written by James Hacker. 331 | 332 | , 1 April 1989 333 | Ty Coon, President of Vice 334 | 335 | This General Public License does not permit incorporating your program into 336 | proprietary programs. If your program is a subroutine library, you may 337 | consider it more useful to permit linking proprietary applications with the 338 | library. If this is what you want to do, use the GNU Lesser General 339 | Public License instead of this License. 340 | 341 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # xboxpy 2 | 3 | [See issues](https://github.com/XboxDev/xboxpy/issues) to find out how to help us! 4 | You can also create a new issue if you have trouble with xboxpy. 5 | 6 | 7 | ### Install 8 | 9 | *If you don't have it already, install [a Python 3 release](https://www.python.org/downloads/) of your choice.* 10 | *Also [ensure you have pip for Python 3 installed](https://pip.pypa.io/en/stable/installing/).* 11 | 12 | Simply run: 13 | 14 | ``` 15 | pip3 install --user -U git+https://github.com/XboxDev/xboxpy.git#egg=xboxpy 16 | ``` 17 | 18 | Now xboxpy should be installed and ready for use! 19 | 20 | 21 | ### Use 22 | 23 | All code is internally imported by the `xboxpy` module. 24 | So all you have to do is: `import xboxpy`. 25 | 26 | You can choose the interface you want to use using the 'XBOX_IF' environment variable: 27 | 28 | * 'XBDM' (default) - part of the official Xbox Development Kit Debug Kernel by Microsoft. 29 | * 'nxdk-rdt' - [nxdk-rdt is an open-source Xbox Remote Dev Tool](https://github.com/XboxDev/nxdk-rdt). 30 | * 'gdb' - can be used to [interface with gdb](https://sourceware.org/gdb/onlinedocs/gdb/Python.html). 31 | * 'none' - does nothing; provided for users which only require xboxpy for logic, but don't need remote access. 32 | 33 | Some interfaces will also allow you to specify the target Xbox using the 'XBOX' environment variable ('Host:Port'). 34 | Not all interfaces support all functionality at this point. 35 | 36 | 37 | ### Develop 38 | 39 | Clone xboxpy [using git](https://git-scm.com/) and install it from the the local folder in editable mode: 40 | 41 | ``` 42 | git clone https://github.com/XboxDev/xboxpy.git 43 | pip3 install --user -e ./xboxpy 44 | ``` 45 | 46 | Now you can make changes to the code locally. 47 | All projects using xboxpy will automatically use your modified version. 48 | 49 | 50 | ### Contribute 51 | 52 | Once you are happy with your changes, you should contribute to the official version of xboxpy! 53 | 54 | [Fork xboxpy on GitHub](https://github.com/XboxDev/xboxpy) and [send a Pull Request to us](https://github.com/XboxDev/xboxpy/pulls). 55 | 56 | 57 | --- 58 | 59 | (c)2018 XboxDev maintainers 60 | 61 | This program is free software; you can redistribute it and/or modify 62 | it under the terms of the GNU General Public License as published by 63 | the Free Software Foundation; either version 2 of the License, or 64 | (at your option) any later version. 65 | 66 | *Contact us for other licensing options.* 67 | -------------------------------------------------------------------------------- /setup.py: -------------------------------------------------------------------------------- 1 | import setuptools 2 | 3 | with open("README.md", "r") as fh: 4 | long_description = fh.read() 5 | 6 | setuptools.setup( 7 | name="xboxpy", 8 | version="0-dev", 9 | maintainer="XboxDev maintainers", 10 | description="Python module to interface with original Xbox hard- and software", 11 | long_description=long_description, 12 | long_description_content_type="text/markdown", 13 | url="https://github.com/XboxDev/xboxpy", 14 | packages=setuptools.find_packages(), 15 | classifiers=( 16 | "Programming Language :: Python :: 3", 17 | "License :: OSI Approved :: GNU General Public License v2 or later (GPLv2+)", 18 | "Operating System :: OS Independent", 19 | "Intended Audience :: Developers", 20 | "Topic :: Software Development :: Libraries :: Python Modules", 21 | ), 22 | ) 23 | -------------------------------------------------------------------------------- /xboxpy/__init__.py: -------------------------------------------------------------------------------- 1 | from .helper.fields import * 2 | 3 | from . import interface 4 | 5 | # Load the core components 6 | from . import memory 7 | 8 | # Load helper functions 9 | #FIXME: Move stuff into namespaces 10 | from .pe import * #from . import pe 11 | from . import aci 12 | from . import apu 13 | from .apu_regs import * 14 | from . import dsp 15 | from . import ke 16 | from . import nv2a 17 | -------------------------------------------------------------------------------- /xboxpy/aci.py: -------------------------------------------------------------------------------- 1 | from . import * 2 | from . import memory 3 | from .helper import better_wave 4 | import time 5 | 6 | def read_u8(address): 7 | return memory.read_u8(0xFEC00000 + address) 8 | def read_u16(address): 9 | return memory.read_u16(0xFEC00000 + address) 10 | def read_u32(address): 11 | return memory.read_u32(0xFEC00000 + address) 12 | 13 | def write_u8(address, value): 14 | memory.write_u8(0xFEC00000 + address, value) 15 | def write_u16(address, value): 16 | memory.write_u16(0xFEC00000 + address, value) 17 | def write_u32(address, value): 18 | memory.write_u32(0xFEC00000 + address, value) 19 | 20 | 21 | def export_wav(path, channels=2, sample_width=2, sample_rate=48000, fmt=better_wave.WAVE_FORMAT_PCM): 22 | # Also see https://github.com/Sergeanur/XboxADPCM/blob/master/XboxADPCM/XboxADPCM.cpp for ADPCM 23 | wav = better_wave.open(path, 'wb') 24 | wav.setformat(fmt) 25 | wav.setnchannels(channels) 26 | wav.setsampwidth(sample_width) 27 | wav.setframerate(sample_rate) 28 | return wav 29 | 30 | def ac97_status(): 31 | print("global control=0x" + format(read_u32(0x12C), '08X')) 32 | print("global status=0x" + format(read_u32(0x130), '08X')) 33 | def dump_buffers(addr,name): 34 | descriptor = read_u32(addr) 35 | print("??? desc is p 0x" + format(descriptor, '08X')) 36 | descriptor |= 0x80000000 37 | # FIXME: Download all descriptors in one packet and then parse here 38 | wav = export_wav(format(addr, 'X') + ".wav") 39 | for i in range(0, 32): 40 | addr = memory.read_u32(descriptor + i * 8 + 0) 41 | length = memory.read_u16(descriptor + i * 8 + 4) 42 | control = memory.read_u16(descriptor + i * 8 + 6) 43 | if (addr != 0) or (length != 0) or (control != 0): 44 | print(str(i) + ": 0x" + format(addr, '08X') + " (" + str(length) + " samples); control: 0x" + format(control, '04X')) 45 | addr |= 0x80000000 46 | data = memory.read(addr, length * 2) 47 | wav.writeframes(data) 48 | wav.close() 49 | print("CIV=0x" + format(read_u8(addr + 0x4), '02X')) 50 | print("LVI=0x" + format(read_u8(addr + 0x5), '02X')) 51 | print("SR=0x" + format(read_u16(addr + 0x6), '04X')) 52 | print("pos=0x" + format(read_u16(addr + 0x8), '04X')) 53 | print("piv=0x" + format(read_u16(addr + 0xA), '04X')) 54 | print("CR=0x" + format(read_u8(addr + 0xB), '02X')) 55 | dump_buffers(0x110, "pcm.wav") 56 | dump_buffers(0x170, "spdif.wav") 57 | 58 | def TraceAC97(callback): 59 | descriptor = read_u32(0x110) 60 | print("??? desc is p 0x" + format(descriptor, '08X')) 61 | descriptor |= 0x80000000 62 | for i in range(0, 32): 63 | addr = memory.read_u32(descriptor + i * 8 + 0) 64 | length = memory.read_u16(descriptor + i * 8 + 4) 65 | control = memory.read_u16(descriptor + i * 8 + 6) 66 | if (addr != 0) or (length != 0) or (control != 0): 67 | print(str(i) + ": 0x" + format(addr, '08X') + " (" + str(length) + " samples); control: 0x" + format(control, '04X')) 68 | addr |= 0x80000000 69 | current_milli_time = lambda: time.time() * 1000.0 70 | buffer_size = length * 2 71 | chunk_size = 1024 # Hardare runs at this chunk size 72 | catchup = 0 73 | 74 | # Wait for start of playback 75 | sample_rate = 48000.0 76 | time_for_buffer = (length / 2) / sample_rate * 1000.0 77 | time_per_chunk = time_for_buffer / (buffer_size / chunk_size) 78 | 79 | #FIXME: This was an experiment on how long it would take to dump the entire buffer 80 | # We can probably dump the entire buffer and then just grab the data we need 81 | # [as we can follow the write cursor by diff'ing the buffers] 82 | if False: 83 | prev_data = None 84 | start = current_milli_time() 85 | bt = 0 86 | min_delta = 999999999 87 | min_non_delta = 0 88 | t_last = None 89 | while(True): 90 | data = memory.read(addr, buffer_size) 91 | diff = "" 92 | blocks = 165 93 | block_size = buffer_size / blocks 94 | if prev_data != None: 95 | 96 | if True: 97 | first = buffer_size - 1 98 | last = 0 99 | for i in range(0, buffer_size): 100 | if data[i] != prev_data[i]: 101 | first = min(i, first) 102 | last = max(i, last) 103 | t = (current_milli_time() - start) * 1000 104 | if t_last: 105 | delta = t - t_last 106 | ts = str(int(t)).rjust(7) 107 | size = max(last - first + 1, 0) 108 | if size >= 8000: 109 | bt = t 110 | if (size == 0): 111 | min_non_delta = max(delta, min_non_delta) 112 | msg = ts + ": No changes" 113 | else: 114 | # Find the shortest delta 115 | min_delta = min(delta, min_delta) 116 | msg = ts + ": From " + str(first) + " to " + str(last) + " (" + str(size) + " bytes)" 117 | of = t - bt # Offset in frame 118 | print(msg.ljust(50) + "+" + str(int(of)).ljust(5) + " (dt: " + str(int(delta)).ljust(5) + " min-dt: " + str(int(min_delta)) + " / non-dt: " + str(int(min_non_delta)) + ")") 119 | t_last = t 120 | else: 121 | for i in range(0, blocks): 122 | idx = int(block_size*i) 123 | if data[idx:int(idx+block_size)] == prev_data[idx:int(idx+block_size)]: 124 | diff += ' ' 125 | else: 126 | diff += '#' 127 | print("[" + diff + "]") 128 | prev_data = data 129 | took = current_milli_time() - start 130 | #print("Took " + str(took) + " / " + str(time_for_buffer) + " ms") 131 | 132 | 133 | # This is a poc where I try to find out when buffers are written 134 | #FIXME: This should sit somewhere in the streaming loop so we can resync 135 | print("Waiting for playback") 136 | last = None 137 | while True: 138 | new = memory.read_u32(addr) 139 | if last != None and last != new: 140 | print("Playback started!") 141 | time.sleep(time_for_buffer * 0.5 / 1000.0) # Give it another half buffer headstart 142 | # Playback reached us! Wait until the write cursor reaches the other half of the buffer 143 | break 144 | last = new 145 | 146 | underruns = 0 147 | offset = 512 # Hardware buffer starts at 512 byte offset 148 | 149 | first_start = time.time() 150 | while True: 151 | offset %= buffer_size 152 | start = current_milli_time() 153 | 154 | streamed_data = bytearray() 155 | 156 | to_end = buffer_size - offset 157 | data = memory.read(addr + offset, min(chunk_size, to_end)) 158 | remaining = chunk_size - len(data) 159 | if remaining > 0: 160 | data += memory.read(addr, remaining) 161 | 162 | offset += chunk_size 163 | 164 | # Handle output in callback 165 | if callback(time.time() - first_start, bytes(data)): 166 | break 167 | 168 | took = current_milli_time() - start 169 | remain = time_per_chunk - took 170 | remain -= catchup 171 | if remain > 0: 172 | time.sleep(remain / 1000.0) 173 | took = current_milli_time() - start 174 | remain = time_per_chunk - took 175 | else: 176 | underruns += 1 177 | catchup -= remain 178 | #print("too slow!") 179 | 180 | print("Had " + str(underruns) + " underruns") 181 | 182 | -------------------------------------------------------------------------------- /xboxpy/apu.py: -------------------------------------------------------------------------------- 1 | from . import * 2 | from . import memory 3 | from .apu_regs import * 4 | from .aci import export_wav 5 | import math 6 | import time 7 | 8 | def read_u8(address): 9 | return memory.read_u8(0xFE800000 + address) 10 | def read_u16(address): 11 | return memory.read_u16(0xFE800000 + address) 12 | def read_u32(address): 13 | return memory.read_u32(0xFE800000 + address) 14 | 15 | def write_u8(address, value): 16 | memory.write_u8(0xFE800000 + address, value) 17 | def write_u16(address, value): 18 | memory.write_u16(0xFE800000 + address, value) 19 | def write_u32(address, value): 20 | memory.write_u32(0xFE800000 + address, value) 21 | 22 | 23 | def mem(): 24 | def dump_mem(name, index): 25 | desc_addr = read_u32(0x2040 + index) 26 | print(name + " addr: 0x" + format(desc_addr, '08X')) # only the upper bits are used [aligned by 0x4000 bytes?!] 27 | desc_length = read_u32(0x20D4 + index) 28 | print(name + " length: " + str(desc_length)) # 16 bits only 29 | wav = export_wav("dsp-" + name + ".wav") 30 | desc_addr |= 0x80000000 31 | for i in range(0, desc_length): 32 | addr = memory.read_u32(desc_addr + i * 8 + 0) 33 | 34 | print(" Address: 0x" + format(addr, '08X') + "; Flags: 0x" + format(flags, '04X') + "; Length: " + str(length)) 35 | 36 | addr |= 0x80000000 37 | data = memory.read(addr, length) 38 | wav.writeframes(data) 39 | wav.close() 40 | 41 | if False: 42 | dump_mem("EPF", 12) 43 | dump_mem("EPS", 8) 44 | dump_mem("GPF", 4) 45 | dump_mem("GPS", 0) 46 | 47 | vpvaddr = read_u32(NV_PAPU_VPVADDR) 48 | vpsgeaddr = read_u32(NV_PAPU_VPSGEADDR) 49 | vpssladdr = read_u32(NV_PAPU_VPSSLADDR) 50 | vphtaddr = read_u32(NV_PAPU_VPHTADDR) 51 | vphcaddr = read_u32(NV_PAPU_VPHCADDR) 52 | gpsaddr = read_u32(NV_PAPU_GPSADDR) 53 | gpfaddr = read_u32(NV_PAPU_GPFADDR) 54 | epsaddr = read_u32(NV_PAPU_EPSADDR) 55 | epfaddr = read_u32(NV_PAPU_EPFADDR) 56 | 57 | def dump_mem_map(name, addr, sge_count = 0): 58 | print(name + ": 0x" + format(addr, '08X')) 59 | if sge_count == 0: 60 | return 61 | length = 0 62 | for i in range(0, sge_count + 1): # Last one is just to dump out data 63 | if i < sge_count: 64 | data_addr = memory.read_u32(0x80000000 | addr + i * 8 + 0) 65 | data_flags = memory.read_u16(0x80000000 | addr + i * 8 + 4) 66 | data_length = memory.read_u16(0x80000000 | addr + i * 8 + 6) 67 | addr_from = i * 0x1000 68 | # Be smart and combine multiple entries into one.. 69 | if (i > 0 and (data_addr != last_data_addr + 0x1000 or data_flags != last_data_flags)) or i == sge_count: 70 | print(" 0x" + format(addr_from - length, '06X') + " - 0x" + format(addr_from - 1, '06X') + " maps to RAM 0x" + format(last_data_addr - length, '08X') + " - 0x" + format(last_data_addr + 0xFFF, '08X') + " flags: 0x" + format(last_data_flags, '04X')) 71 | length = 0 72 | length += 0x1000 73 | assert(data_flags == 0) 74 | assert(data_length == 0) #FIXME: This should be enabled, but the last entries are sometimes bogus?! 75 | last_data_addr = data_addr 76 | last_data_flags = data_flags 77 | 78 | gpsmaxsge = read_u32(NV_PAPU_GPSMAXSGE) & 0xFFFF 79 | gpfmaxsge = read_u32(NV_PAPU_GPFMAXSGE) & 0xFFFF 80 | epsmaxsge = read_u32(NV_PAPU_EPSMAXSGE) & 0xFFFF 81 | epfmaxsge = read_u32(NV_PAPU_EPFMAXSGE) & 0xFFFF 82 | 83 | dump_mem_map("NV_PAPU_VPVADDR", vpvaddr) 84 | dump_mem_map("NV_PAPU_VPSGEADDR", vpsgeaddr, 0x1000 // 8) 85 | dump_mem_map("NV_PAPU_VPSSLADDR", vpssladdr) 86 | dump_mem_map("NV_PAPU_VPHTADDR", vphtaddr) 87 | dump_mem_map("NV_PAPU_VPHCADDR", vphcaddr) 88 | # This looks like an off-by-one in the MS code?! 89 | # (Should be MAXSGE+1 but then we get bogus data) 90 | #FIXME: Test on hardware 91 | dump_mem_map("NV_PAPU_GPSADDR", gpsaddr, gpsmaxsge) 92 | dump_mem_map("NV_PAPU_GPFADDR", gpfaddr, gpfmaxsge) 93 | dump_mem_map("NV_PAPU_EPSADDR", epsaddr, epsmaxsge) 94 | dump_mem_map("NV_PAPU_EPFADDR", epfaddr, epfmaxsge) 95 | 96 | 97 | #FIXME: Take argument for max_sge 98 | def read_mem(sge_addr, base, length): 99 | #FIXME: Also make work with odd data 100 | data = bytearray() 101 | address = base 102 | remaining = length 103 | while remaining > 0: 104 | page = address // 0x1000 105 | offset = address % 0x1000 106 | data_addr = memory.read_u32(0x80000000 | sge_addr + page * 8 + 0) 107 | data += memory.read(0x80000000 | data_addr + offset, min(remaining, 0x1000 - offset)) 108 | remaining = length - len(data) 109 | return data 110 | 111 | def write_mem(sge_addr, base, data): 112 | #FIXME: Also make work with odd data 113 | assert(base & 0xFFF == 0) 114 | first_sge_idx = base // 0x1000 115 | for idx in range(first_sge_idx, first_sge_idx + len(data) // 0x1000 + 1): 116 | data_addr = memory.read_u32(0x80000000 | sge_addr + idx * 8 + 0) 117 | memory.write(0x80000000 | data_addr, data[0x1000 * idx:0x1000 * idx + 0x1000]) 118 | 119 | def fifos(dump = False): 120 | def dump_fifo(name, addr, sge_addr): 121 | base = read_u32(addr + 0) & 0x00FFFF00 122 | end = read_u32(addr + 4) & 0x00FFFF00 123 | cur = read_u32(addr + 8) & 0x00FFFFFC 124 | print(name + " BASE=0x" + format(base, '08X') + " END=0x" + format(end, '08X') + " CUR=0x" + format(cur, '08X')) 125 | if dump: 126 | data = read_mem(sge_addr, base, end - base) 127 | wav = export_wav(name + ".wav") 128 | wav.writeframes(data) 129 | wav.close() 130 | 131 | # Check out FIFOs 132 | 133 | gpfaddr = read_u32(NV_PAPU_GPFADDR) 134 | for i in range(0,4): 135 | dump_fifo("GPOF" + str(i), NV_PAPU_GPOF0 + i * 0x10, gpfaddr) 136 | print("") 137 | for i in range(0,2): 138 | dump_fifo("GPIF" + str(i), NV_PAPU_GPIF0 + i * 0x10, gpfaddr) 139 | print("") 140 | 141 | epfaddr = read_u32(NV_PAPU_EPFADDR) 142 | for i in range(0,4): 143 | dump_fifo("EPOF" + str(i), NV_PAPU_EPOF0 + i * 0x10, epfaddr) 144 | print("") 145 | for i in range(0,2): 146 | dump_fifo("EPIF" + str(i), NV_PAPU_EPIF0 + i * 0x10, epfaddr) 147 | print("") 148 | 149 | #FIXME: Keep padding byte there and instead provide conversion routines 150 | def read_dsp_mem(addr, length): 151 | assert(length % 4 == 0) 152 | data = bytearray() 153 | if True: 154 | # FIXME: This code path might not work in XQEMU! 155 | data = memory.read(0xFE800000 + addr, length) 156 | else: 157 | for i in range(0, length // 4): 158 | value = read_u32(addr + i * 4) 159 | data += int.to_bytes(value & 0xFFFFFF, length=4, byteorder='little') 160 | return bytes(data) 161 | 162 | def write_dsp_mem(addr, data): 163 | assert(len(data) % 4 == 0) 164 | for i in range(0, len(data) // 4): 165 | value = int.from_bytes(data[i*4:i*4+3], byteorder='little', signed=False) 166 | write_u32(addr + i * 4, value) 167 | 168 | def gp(): 169 | gpsaddr = read_u32(NV_PAPU_GPSADDR) 170 | 171 | data = bytearray() 172 | seconds = 2 173 | start = time.time() 174 | duration = 0 175 | 176 | #FIXME: Hack for performance 177 | addr = 0x9000 # See http://xboxdevwiki.net/APU#Usage_in_DirectSound for the channel order 178 | data_addr = memory.read_u32(0x80000000 | gpsaddr + (addr // 0x1000) * 8 + 0) 179 | for i in range(0, seconds * 48000 // 0x20): 180 | #FIXME: Improve performance of the following: 181 | #data += read_mem(gpsaddr, 0x8000, 256) 182 | 183 | data += memory.read(0x80000000 | data_addr + (addr % 0x1000) + (0x20 * 4 * i) % 0x800, 0x20 * 4) 184 | while duration < i * 0x20 / 48000: 185 | duration = time.time() - start 186 | data = to_dsp(data) 187 | 188 | print("Took " + str(duration * 1000.0) + "ms. Expected " + str(seconds * 1000.0) + "ms") 189 | 190 | wav = export_wav("GP-outmaybemaybenot.wav", channels=1, sample_width=3) 191 | wav.writeframes(data) 192 | wav.close() 193 | 194 | ##FIXME: Code removed here 195 | 196 | # Dump GP Memory (aside from MIXBUF) 197 | 198 | wav = export_wav("GP-XMEM.wav") 199 | data = read_dsp_mem(NV_PAPU_GPXMEM, 0x1000 * 4) 200 | wav.writeframes(data) 201 | wav.close() 202 | wav = export_wav("GP-YMEM.wav") 203 | data = read_dsp_mem(NV_PAPU_GPYMEM, 0x800 * 4) 204 | wav.writeframes(data) 205 | wav.close() 206 | wav = export_wav("GP-PMEM.wav") 207 | data = read_dsp_mem(NV_PAPU_GPPMEM, 0x1000 * 4) 208 | wav.writeframes(data) 209 | wav.close() 210 | 211 | def TraceMIXBUF(bin_index, callback): 212 | assert(bin_index >= 0) 213 | assert(bin_index <= 31) 214 | 215 | #FIXME: Support dumping multiple bins at once? 216 | # MIXBUF is 0x400 = 32 bins * 0x20 words which contain 48kHz 24-bit PCM 217 | start = time.time() 218 | duration = 0 219 | step = 1 220 | while(True): 221 | data = read_dsp_mem(NV_PAPU_GPMIXBUF + bin_index * 0x20 * 4, 0x20 * 4) 222 | # Handle output in callback 223 | if callback(duration, bytes(data)): 224 | break 225 | while duration < step * 0x20 / 48000: 226 | duration = time.time() - start 227 | step += 1 228 | 229 | #FIXME: Untested since moving code around, probably broken! 230 | # Use 'tiny-gp.inc' as DSP code to avoid running DirectSound 231 | def InjectGPOutput(hook_code = False): 232 | if hook_code: 233 | #FIXME: Move code to turn off / hijack DSP into other functions 234 | #FIXME: Turn off GP DSP 235 | write_u32(NV_PAPU_GPRST, NV_PAPU_GPRST_GPRST) 236 | 237 | code_data = from_dsp(load_dsp_code("a56.out")) 238 | code_backup = read_dsp_mem(NV_PAPU_GPPMEM, len(code_data)) 239 | scratch_backup = read_mem(gpsaddr, 0, len(code_data)) 240 | write_dsp_mem(NV_PAPU_GPPMEM, code_data) 241 | write_mem(gpsaddr, 0, code_data) 242 | print("Patched code!") 243 | 244 | # Re-Enable the GP DSP 245 | write_u32(NV_PAPU_GPRST, NV_PAPU_GPRST_GPRST | NV_PAPU_GPRST_GPDSPRST) 246 | 247 | # memory.write GP output scratch space while GP DSP is off (Does not work too good yet) 248 | addr = 0x8000 # See http://xboxdevwiki.net/APU#Usage_in_DirectSound for the channel order 249 | data_addr = memory.read_u32(0x80000000 | gpsaddr + (addr // 0x1000) * 8 + 0) 250 | data = bytearray() 251 | for i in range(0, seconds * 48000 // 0x20): 252 | chunk = bytearray() 253 | for j in range(0, 0x20): 254 | t = (i * 0x20 + j) / 48000 255 | chunk += int.to_bytes(int(0x1FFFFF * math.sin(t * math.pi * 2 * 500)), signed=True, length=3, byteorder='little') + bytes([0]) 256 | data += chunk 257 | while duration < i * 0x20 / 48000: 258 | duration = time.time() - start 259 | assert(len(chunk) == 0x20 * 4) 260 | memory.write(0x80000000 | data_addr + (addr % 0x1000) + (0x20 * 4 * i) % 0x800, chunk) 261 | data = to_dsp(data) 262 | print("Took " + str(duration * 1000.0) + "ms. Expected " + str(seconds * 1000.0) + "ms") 263 | wav = export_wav("GP-injected.wav", channels=1, sample_width=3) 264 | wav.writeframes(data) 265 | wav.close() 266 | 267 | if hook_code: 268 | # Check if we ran the correct code 269 | code_verify = read_dsp_mem(NV_PAPU_GPPMEM, len(code_data)) 270 | if code_verify != code_data: 271 | print("Oops! Code was not written successfully!") 272 | 273 | # Resume normal DSP operation 274 | write_u32(NV_PAPU_GPRST, NV_PAPU_GPRST_GPRST) 275 | write_dsp_mem(NV_PAPU_GPPMEM, code_backup) 276 | write_mem(gpsaddr, 0, scratch_backup) 277 | print("Recovered!") 278 | write_u32(NV_PAPU_GPRST, NV_PAPU_GPRST_GPRST | NV_PAPU_GPRST_GPDSPRST) 279 | 280 | def InvalidateCache(offset = None): 281 | pass #FIXME: Auto invalidate on writes near offset with apu functions 282 | 283 | def RetrievePointer(register): 284 | #FIXME: Cache these values! 285 | return read_u32(register) 286 | 287 | def ReadSSL(offset, length): 288 | pass 289 | 290 | def _MapSGE(vpsgeaddr, offset): 291 | def vp_sge(sge_handle): 292 | return memory.read_u32(vpsgeaddr + sge_handle * 8, True) 293 | page_index = offset // 0x1000 294 | offset_in_page = offset % 0x1000 295 | page_base = vp_sge(page_index) 296 | return page_base + offset_in_page 297 | 298 | def ReadSGE(offset, length): 299 | vpsgeaddr = RetrievePointer(NV_PAPU_VPSGEADDR) 300 | out = bytearray() 301 | while length > 0: 302 | paged = _MapSGE(vpsgeaddr, offset) 303 | in_page = 0x1000 - (paged % 0x1000) 304 | chunk_size = min(in_page, length) 305 | # FIXME: Somehow handle this permission stuff differently 306 | mapped = map_page(0x80000000 | paged, True) 307 | data = memory.read(paged, chunk_size, True) 308 | map_page(0x80000000 | paged, mapped) 309 | out += data 310 | length -= chunk_size 311 | offset += chunk_size 312 | return bytes(out) 313 | 314 | def WriteSGE(offset, data): 315 | vpsgeaddr = RetrievePointer(NV_PAPU_VPSGEADDR) 316 | while True: 317 | remaining = len(data) - offset 318 | if remaining == 0: 319 | break 320 | paged = _MapSGE(vpsgeaddr, offset) 321 | in_page = 0x1000 - (paged % 0x1000) 322 | chunk_size = min(in_page, remaining) 323 | # FIXME: Somehow handle this permission stuff differently 324 | mapped = map_page(0x80000000 | paged, True) 325 | memory.write(paged, data[-remaining:-remaining + chunk_size], True) 326 | map_page(0x80000000 | paged, mapped) 327 | offset += chunk_size 328 | return 329 | 330 | def ReadVoice(voice_handle, field_offset, field_mask): 331 | vpvaddr = RetrievePointer(NV_PAPU_VPVADDR) #FIXME: Pass as string so we can look it up in a cache more easily 'NV_PAPU_VPVADDR' 332 | value = memory.read_u32(vpvaddr + voice_handle * NV_PAVS_SIZE + field_offset, True); # FIXME: Physical address 333 | return GetMasked(value, field_mask) 334 | 335 | def WriteVoice(voice_handle, field_offset, field_mask, value): 336 | vpvaddr = RetrievePointer(NV_PAPU_VPVADDR) #FIXME: Pass as string so we can look it up in a cache more easily 'NV_PAPU_VPVADDR' 337 | old_value = memory.read_u32(vpvaddr + voice_handle * NV_PAVS_SIZE + field_offset, True); # FIXME: Physical address 338 | new_value = SetMasked(old_value, field_mask, value) 339 | memory.write_u32(vpvaddr + voice_handle * NV_PAVS_SIZE + field_offset, new_value, True); # FIXME: Physical address 340 | 341 | # This dumps information about the active voice, usually not of interest 342 | if False: 343 | NV_PAPU_FEAV_LST_values = ['INHERIT', '2D_TOP', '3D_TOP', 'MP_TOP'] 344 | feav = read_u32(NV_PAPU_FEAV) 345 | active_voice = feav & NV_PAPU_FEAV_VALUE 346 | feav_lst = (feav & NV_PAPU_FEAV_LST) >> 16 347 | print("Active List: " + str(feav_lst) + " (" + NV_PAPU_FEAV_LST_values[feav_lst] + ")") 348 | 349 | # p = 4096*log2(f/48000) 350 | def PitchToFrequency(pitch): 351 | return 2 ** (pitch / 4096) * 48000 352 | 353 | # f = 2^(p/4096)*48000 354 | def FrequencyToPitch(frequency): 355 | assert(False) # FIXME 356 | 357 | #FIXME: Take a voice_handle as argument instead 358 | def IterateVoices(addr, callback, name=None): 359 | top_voice = read_u32(addr) 360 | # print(name + " Top voice: 0x" + format(top_voice, '04X')) 361 | next_voice = top_voice 362 | while next_voice != 0xFFFF: 363 | _next_voice = callback(next_voice, name) 364 | if next_voice == _next_voice: 365 | assert(False) 366 | return 367 | next_voice = _next_voice 368 | 369 | def IterateVoiceLists(callback): 370 | IterateVoices(NV_PAPU_TVL2D, callback, name="2D") 371 | IterateVoices(NV_PAPU_TVL3D, callback, name="3D") 372 | IterateVoices(NV_PAPU_TVLMP, callback, name="MP") 373 | -------------------------------------------------------------------------------- /xboxpy/apu_regs.py: -------------------------------------------------------------------------------- 1 | NV_PAPU_FEAV = 0x1118 2 | NV_PAPU_FEAV_VALUE = 0x0000FFFF 3 | NV_PAPU_FEAV_LST = 0x00030000 4 | 5 | NV_PAPU_VPVADDR = 0x202C 6 | NV_PAPU_VPSGEADDR = 0x2030 7 | NV_PAPU_VPSSLADDR = 0x2034 8 | NV_PAPU_VPHTADDR = 0x2038 9 | NV_PAPU_VPHCADDR = 0x203C 10 | NV_PAPU_GPSADDR = 0x2040 11 | NV_PAPU_GPFADDR = 0x2044 12 | NV_PAPU_EPSADDR = 0x2048 13 | NV_PAPU_EPFADDR = 0x204C 14 | 15 | NV_PAPU_GPSMAXSGE = 0x20D4 16 | NV_PAPU_GPFMAXSGE = 0x20D8 17 | NV_PAPU_EPSMAXSGE = 0x20DC 18 | NV_PAPU_EPFMAXSGE = 0x20E0 19 | 20 | NV_PAPU_EPXMEM = 0x50000 21 | NV_PAPU_EPYMEM = 0x56000 22 | NV_PAPU_EPPMEM = 0x5A000 23 | 24 | NV_PAPU_GPXMEM = 0x30000 25 | NV_PAPU_GPMIXBUF = 0x35000 26 | NV_PAPU_GPYMEM = 0x36000 27 | NV_PAPU_GPPMEM = 0x3A000 28 | 29 | NV_PAPU_GPRST = 0x3FFFC 30 | NV_PAPU_GPRST_GPRST = (1 << 0) 31 | NV_PAPU_GPRST_GPDSPRST = (1 << 1) 32 | 33 | NV_PAPU_EPRST = 0x5FFFC 34 | NV_PAPU_EPRST_EPRST = 0x00000001 35 | NV_PAPU_EPRST_EPDSPRST = 0x00000002 36 | 37 | NV_PAPU_GPOF0 = 0x3024 # 4 x 0x10 pitch 38 | NV_PAPU_GPIF0 = 0x3064 # 2 x 0x10 pitch 39 | 40 | NV_PAPU_EPOF0 = 0x4024 # 4 x 0x10 pitch 41 | NV_PAPU_EPIF0 = 0x4064 # 2 x 0x10 pitch 42 | 43 | NV_PAPU_TVL2D = 0x2054 44 | NV_PAPU_TVL3D = 0x2060 45 | NV_PAPU_TVLMP = 0x206C 46 | 47 | 48 | NV_PAVS_SIZE = 0x80 49 | 50 | NV_PAVS_VOICE_CFG_VBIN = 0x00 51 | NV_PAVS_VOICE_CFG_FMT = 0x04 52 | NV_PAVS_VOICE_CFG_FMT_SAMPLES_PER_BLOCK = (20,16) 53 | NV_PAVS_VOICE_CFG_FMT_MULTIPASS = (21,21) 54 | NV_PAVS_VOICE_CFG_FMT_LINKED_VOICE = (22,22) 55 | NV_PAVS_VOICE_CFG_FMT_PERSIST = (23,23) 56 | NV_PAVS_VOICE_CFG_FMT_DATA_TYPE = (24,24) 57 | NV_PAVS_VOICE_CFG_FMT_LOOP = (25, 25) 58 | NV_PAVS_VOICE_CFG_FMT_STEREO = (27,27) 59 | NV_PAVS_VOICE_CFG_FMT_SAMPLE_SIZE = (29,28) 60 | NV_PAVS_VOICE_CFG_FMT_CONTAINER_SIZE = (31,30) 61 | NV_PAVS_VOICE_CFG_ENV0 = 0x08 62 | NV_PAVS_VOICE_CFG_ENV0_EA_ATTACKRATE = (11, 0) 63 | NV_PAVS_VOICE_CFG_ENV0_EA_DELAYTIME = (23, 12) 64 | NV_PAVS_VOICE_CFG_ENV0_EF_PITCHSCALE = (31, 24) 65 | NV_PAVS_VOICE_CFG_ENVA = 0x0C 66 | NV_PAVS_VOICE_CFG_ENVA_EA_DECAYRATE = (11, 0) 67 | NV_PAVS_VOICE_CFG_ENVA_EA_HOLDTIME = (23, 12) 68 | NV_PAVS_VOICE_CFG_ENVA_EA_SUSTAINLEVEL = (31, 24) 69 | NV_PAVS_VOICE_CFG_ENV1 = 0x10 70 | NV_PAVS_VOICE_CFG_ENV1_EF_ATTACKRATE = (11, 0) 71 | NV_PAVS_VOICE_CFG_ENV1_EF_DELAYTIME = (23, 12) 72 | NV_PAVS_VOICE_CFG_ENV1_EF_FCSCALE = (31, 24) 73 | NV_PAVS_VOICE_CFG_ENVF = 0x14 74 | NV_PAVS_VOICE_CFG_ENVF_EF_DECAYRATE = (11, 0) 75 | NV_PAVS_VOICE_CFG_ENVF_EF_HOLDTIME = (23, 12) 76 | NV_PAVS_VOICE_CFG_ENVF_EF_SUSTAINLEVEL = (31, 24) 77 | NV_PAVS_VOICE_CFG_MISC = 0x18 # FIXME: Look into this 78 | NV_PAVS_VOICE_CFG_MISC_EF_RELEASERATE = (11, 0) 79 | NV_PAVS_VOICE_CFG_HRTF_TARGET = 0x1C 80 | NV_PAVS_VOICE_CUR_PSL_START = 0x20 # start of buffer? 81 | NV_PAVS_VOICE_CUR_PSL_START_BA = (23, 0) 82 | NV_PAVS_VOICE_CUR_PSL_START_SSLA_COUNT = (7, 0) # Stream 83 | NV_PAVS_VOICE_CUR_PSL_START_SSLA_BASE = (23, 8) # Stream 84 | NV_PAVS_VOICE_CUR_PSL_START_PS_7_0 = (31, 24) 85 | NV_PAVS_VOICE_CUR_PSH_SAMPLE = 0x24 # loop start? 86 | NV_PAVS_VOICE_CUR_PSH_SAMPLE_LBO = (23, 0) # Buffer 87 | NV_PAVS_VOICE_CUR_PSH_SAMPLE_CSI = (7, 0) # Stream (Current stream index?) 88 | NV_PAVS_VOICE_CUR_PSH_SAMPLE_CSSL = (23, 8) # Stream (Current stream segment list?) 89 | NV_PAVS_VOICE_CUR_PSH_SAMPLE_PS_15_8 = (31, 24) 90 | NV_PAVS_VOICE_CUR_VOLA = 0x28 91 | NV_PAVS_VOICE_CUR_VOLB = 0x2C 92 | NV_PAVS_VOICE_CUR_VOLC = 0x30 93 | NV_PAVS_VOICE_CUR_ECNT = 0x34 94 | NV_PAVS_VOICE_CUR_ECNT_EACOUNT = (15, 0) 95 | NV_PAVS_VOICE_CUR_ECNT_EFCOUNT = (31, 16) 96 | NV_PAVS_VOICE_CUR_PRD = 0x38 97 | NV_PAVS_VOICE_CUR_FCA = 0x3C 98 | NV_PAVS_VOICE_CUR_FCB = 0x40 99 | NV_PAVS_VOICE_CUR_FSA = 0x44 100 | NV_PAVS_VOICE_CUR_FSB = 0x48 101 | NV_PAVS_VOICE_CUR_FSC = 0x4C 102 | NV_PAVS_VOICE_PAR_LFO = 0x50 103 | NV_PAVS_VOICE_PAR_STATE = 0x54 104 | NV_PAVS_VOICE_PAR_STATE_PAUSED = (1 << 18) 105 | NV_PAVS_VOICE_PAR_STATE_ACTIVE_VOICE = (1 << 21) 106 | NV_PAVS_VOICE_PAR_STATE_EFCUR = (27, 24) 107 | NV_PAVS_VOICE_PAR_STATE_EFCUR_OFF = 0 108 | NV_PAVS_VOICE_PAR_STATE_EFCUR_DELAY = 1 109 | NV_PAVS_VOICE_PAR_STATE_EFCUR_ATTACK = 2 110 | NV_PAVS_VOICE_PAR_STATE_EFCUR_HOLD = 3 111 | NV_PAVS_VOICE_PAR_STATE_EFCUR_DECAY = 4 112 | NV_PAVS_VOICE_PAR_STATE_EFCUR_SUSTAIN = 5 113 | NV_PAVS_VOICE_PAR_STATE_EFCUR_RELEASE = 6 114 | NV_PAVS_VOICE_PAR_STATE_EFCUR_FORCE_RELEASE = 7 115 | NV_PAVS_VOICE_PAR_STATE_EACUR = (31, 28) 116 | NV_PAVS_VOICE_PAR_STATE_EACUR_OFF = 0 117 | NV_PAVS_VOICE_PAR_STATE_EACUR_DELAY = 1 118 | NV_PAVS_VOICE_PAR_STATE_EACUR_ATTACK = 2 119 | NV_PAVS_VOICE_PAR_STATE_EACUR_HOLD = 3 120 | NV_PAVS_VOICE_PAR_STATE_EACUR_DECAY = 4 121 | NV_PAVS_VOICE_PAR_STATE_EACUR_SUSTAIN = 5 122 | NV_PAVS_VOICE_PAR_STATE_EACUR_RELEASE = 6 123 | NV_PAVS_VOICE_PAR_STATE_EACUR_FORCE_RELEASE = 7 124 | NV_PAVS_VOICE_PAR_OFFSET = 0x58 # current playback offset 125 | NV_PAVS_VOICE_PAR_OFFSET_CBO = (23, 0) # Current Buffer Offset (presumably) 126 | NV_PAVS_VOICE_PAR_OFFSET_CSO = (23, 0) # Current Stream Offset (presumably) 127 | NV_PAVS_VOICE_PAR_OFFSET_EALVL = (31, 24) 128 | NV_PAVS_VOICE_PAR_NEXT = 0x5C # end of buffer? 129 | NV_PAVS_VOICE_PAR_NEXT_EBO = (23, 0) # Buffer 130 | NV_PAVS_VOICE_PAR_NEXT_SSLB_COUNT = (7, 0) # Stream 131 | NV_PAVS_VOICE_PAR_NEXT_SSLB_BASE = (23, 8) # Stream 132 | NV_PAVS_VOICE_PAR_NEXT_EFLVL = (31, 24) # Buffer 133 | NV_PAVS_VOICE_TAR_VOLA = 0x60 134 | NV_PAVS_VOICE_TAR_VOLB = 0x64 135 | NV_PAVS_VOICE_TAR_VOLC = 0x68 136 | NV_PAVS_VOICE_TAR_LFO_ENV = 0x6C 137 | NV_PAVS_VOICE_TAR_LFO_ENV_EA_RELEASERATE = (11, 0) 138 | NV_PAVS_VOICE_TAR_LFO_MOD = 0x70 139 | NV_PAVS_VOICE_TAR_FCA = 0x74 140 | NV_PAVS_VOICE_TAR_FCB = 0x78 141 | NV_PAVS_VOICE_TAR_PITCH_LINK = 0x7c 142 | NV_PAVS_VOICE_TAR_PITCH_LINK_NEXT_VOICE_HANDLE = (15,0) 143 | NV_PAVS_VOICE_TAR_PITCH_LINK_PITCH = (31,16) 144 | 145 | NV_PAPU_SECTL = 0x2000 146 | -------------------------------------------------------------------------------- /xboxpy/dsp.py: -------------------------------------------------------------------------------- 1 | import os 2 | import tempfile 3 | import subprocess 4 | 5 | # 32 bit words to 24 bit words 6 | def to24(data): 7 | assert(len(data) % 4 == 0) 8 | out_data = bytearray(data) 9 | del out_data[3::4] 10 | return bytes(out_data) 11 | 12 | # 24 bit words to 32 bit words 13 | def from24(data, padding=0x00): 14 | assert(len(data) % 3 == 0) 15 | out_data = bytearray() 16 | for i in range(0, len(data) // 3): 17 | out_data += data[i*3:i*3+3] 18 | out_data += bytes([padding]) 19 | return bytes(out_data) 20 | 21 | # Loads a56 assembled machine code into DSP format 22 | def parse_assembler_output(content): 23 | code = bytearray() 24 | curaddr = 0 25 | for line in content.splitlines(): 26 | s = line.split() 27 | assert(len(s) >= 1) 28 | seg = s[0] 29 | if seg == 'I' or seg == 'F': 30 | break 31 | assert(len(s) == 3) 32 | addr = int(s[1],16) 33 | data = int(s[2],16) 34 | assert(addr == curaddr) # Code with gaps not supported 35 | assert(data >= 0 and data <= 0xFFFFFF) 36 | #print("Program data [0x" + format(addr, '04X') + "]: 0x" + format(data, '06X')) 37 | code += int.to_bytes(data, length=3, byteorder='little') 38 | curaddr = addr + 1 39 | return code 40 | 41 | def assemble(code): 42 | # Generate input file 43 | to_a56 = tempfile.NamedTemporaryFile(mode='wb', delete=False) 44 | to_a56.write(bytes(code, encoding='ascii')) 45 | to_a56.close() 46 | 47 | # Generate output filename 48 | from_a56 = tempfile.NamedTemporaryFile(mode='rb', delete=False) 49 | from_a56.close() 50 | 51 | #FIXME: Pipe to variable instead 52 | try: 53 | a56_path = os.environ['A56'] 54 | except: 55 | a56_path = None 56 | a56_command = [a56_path, "-o", from_a56.name, to_a56.name] 57 | 58 | # Run a56 and remove input file when done 59 | a56_state = subprocess.run(a56_command, stdout=subprocess.PIPE, stderr=subprocess.PIPE) 60 | stderr = a56_state.stderr.splitlines() 61 | print(stderr) # Error messages 62 | stdout = a56_state.stdout.splitlines() 63 | print(stdout[-2:]) # Number of errors and warnings 64 | os.unlink(to_a56.name) 65 | 66 | # Retrieve result and remove output file 67 | output = open(from_a56.name).read() 68 | os.unlink(from_a56.name) 69 | 70 | return parse_assembler_output(output) 71 | -------------------------------------------------------------------------------- /xboxpy/helper/__init__.py: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/XboxDev/xboxpy/51ee241f71b2ad708e0f0c5519d5bea538867aaa/xboxpy/helper/__init__.py -------------------------------------------------------------------------------- /xboxpy/helper/better_wave.py: -------------------------------------------------------------------------------- 1 | """Stuff to parse WAVE files. 2 | 3 | Usage. 4 | 5 | Writing WAVE files: 6 | f = wave.open(file, 'w') 7 | where file is either the name of a file or an open file pointer. 8 | The open file pointer must have methods write(), tell(), seek(), and 9 | close(). 10 | 11 | This returns an instance of a class with the following public methods: 12 | setnchannels(n) -- set the number of channels 13 | setsampwidth(n) -- set the sample width 14 | setframerate(n) -- set the frame rate 15 | setnframes(n) -- set the number of frames 16 | setcomptype(type, name) 17 | -- set the compression type and the 18 | human-readable compression type 19 | setparams(tuple) 20 | -- set all parameters at once 21 | tell() -- return current position in output file 22 | writeframesraw(data) 23 | -- write audio frames without pathing up the 24 | file header 25 | writeframes(data) 26 | -- write audio frames and patch up the file header 27 | close() -- patch up the file header and close the 28 | output file 29 | You should set the parameters before the first writeframesraw or 30 | writeframes. The total number of frames does not need to be set, 31 | but when it is set to the correct value, the header does not have to 32 | be patched up. 33 | It is best to first set all parameters, perhaps possibly the 34 | compression type, and then write audio frames using writeframesraw. 35 | When all frames have been written, either call writeframes(b'') or 36 | close() to patch up the sizes in the header. 37 | The close() method is called automatically when the class instance 38 | is destroyed. 39 | """ 40 | 41 | import builtins 42 | 43 | __all__ = ["open", "openfp", "Error", "Wave_write"] 44 | 45 | class Error(Exception): 46 | pass 47 | 48 | WAVE_FORMAT_PCM = 0x0001 49 | WAVE_FORMAT_IMA_ADPCM = 0x0011 50 | WAVE_FORMAT_XBOX_ADPCM = 0x0069 51 | 52 | _array_fmts = None, 'b', 'h', None, 'i' 53 | 54 | import audioop 55 | import struct 56 | import sys 57 | from chunk import Chunk 58 | from collections import namedtuple 59 | 60 | _wave_params = namedtuple('_wave_params', 61 | 'nchannels sampwidth framerate nframes comptype compname') 62 | 63 | class Wave_write: 64 | """Variables used in this class: 65 | 66 | These variables are user settable through appropriate methods 67 | of this class: 68 | _file -- the open file with methods write(), close(), tell(), seek() 69 | set through the __init__() method 70 | _comptype -- the AIFF-C compression type ('NONE' in AIFF) 71 | set through the setcomptype() or setparams() method 72 | _compname -- the human-readable AIFF-C compression type 73 | set through the setcomptype() or setparams() method 74 | _nchannels -- the number of audio channels 75 | set through the setnchannels() or setparams() method 76 | _sampwidth -- the number of bytes per audio sample 77 | set through the setsampwidth() or setparams() method 78 | _framerate -- the sampling frequency 79 | set through the setframerate() or setparams() method 80 | _nframes -- the number of audio frames written to the header 81 | set through the setnframes() or setparams() method 82 | 83 | These variables are used internally only: 84 | _datalength -- the size of the audio samples written to the header 85 | _nframeswritten -- the number of frames actually written 86 | _datawritten -- the size of the audio samples actually written 87 | """ 88 | 89 | def __init__(self, f): 90 | self._i_opened_the_file = None 91 | if isinstance(f, str): 92 | f = builtins.open(f, 'wb') 93 | self._i_opened_the_file = f 94 | try: 95 | self.initfp(f) 96 | except: 97 | if self._i_opened_the_file: 98 | f.close() 99 | raise 100 | 101 | def initfp(self, file): 102 | self._file = file 103 | self._convert = None 104 | self._format = 0 105 | self._extra = 0 106 | self._nchannels = 0 107 | self._sampwidth = 0 108 | self._framerate = 0 109 | self._nframes = 0 110 | self._nframeswritten = 0 111 | self._datawritten = 0 112 | self._datalength = 0 113 | self._headerwritten = False 114 | 115 | def __del__(self): 116 | self.close() 117 | 118 | def __enter__(self): 119 | return self 120 | 121 | def __exit__(self, *args): 122 | self.close() 123 | 124 | # 125 | # User visible methods. 126 | # 127 | def setnchannels(self, nchannels): 128 | if self._datawritten: 129 | raise Error('cannot change parameters after starting to write') 130 | if nchannels < 1: 131 | raise Error('bad # of channels') 132 | self._nchannels = nchannels 133 | 134 | def getnchannels(self): 135 | if not self._nchannels: 136 | raise Error('number of channels not set') 137 | return self._nchannels 138 | 139 | def setformat(self, format): 140 | if self._datawritten: 141 | raise Error('cannot change parameters after starting to write') 142 | self._format = format 143 | 144 | def getformat(self): 145 | if not self._format: 146 | raise Error('format not set') 147 | return self._format 148 | 149 | def setsampwidth(self, sampwidth): 150 | if self._datawritten: 151 | raise Error('cannot change parameters after starting to write') 152 | if sampwidth < 1 or sampwidth > 4: 153 | raise Error('bad sample width') 154 | self._sampwidth = sampwidth 155 | 156 | def getsampwidth(self): 157 | if not self._sampwidth: 158 | raise Error('sample width not set') 159 | return self._sampwidth 160 | 161 | def setframerate(self, framerate): 162 | if self._datawritten: 163 | raise Error('cannot change parameters after starting to write') 164 | if framerate <= 0: 165 | raise Error('bad frame rate') 166 | self._framerate = int(round(framerate)) 167 | 168 | def getframerate(self): 169 | if not self._framerate: 170 | raise Error('frame rate not set') 171 | return self._framerate 172 | 173 | def setnframes(self, nframes): 174 | if self._datawritten: 175 | raise Error('cannot change parameters after starting to write') 176 | self._nframes = nframes 177 | 178 | def getnframes(self): 179 | return self._nframeswritten 180 | 181 | def setcomptype(self, comptype, compname): 182 | if self._datawritten: 183 | raise Error('cannot change parameters after starting to write') 184 | if comptype not in ('NONE',): 185 | raise Error('unsupported compression type') 186 | self._comptype = comptype 187 | self._compname = compname 188 | 189 | def getcomptype(self): 190 | return self._comptype 191 | 192 | def getcompname(self): 193 | return self._compname 194 | 195 | def setparams(self, params): 196 | nchannels, sampwidth, framerate, nframes, comptype, compname = params 197 | if self._datawritten: 198 | raise Error('cannot change parameters after starting to write') 199 | self.setnchannels(nchannels) 200 | self.setsampwidth(sampwidth) 201 | self.setframerate(framerate) 202 | self.setnframes(nframes) 203 | self.setcomptype(comptype, compname) 204 | 205 | def getparams(self): 206 | if not self._nchannels or not self._sampwidth or not self._framerate: 207 | raise Error('not all parameters set') 208 | return _wave_params(self._nchannels, self._sampwidth, self._framerate, 209 | self._nframes, self._comptype, self._compname) 210 | 211 | def setmark(self, id, pos, name): 212 | raise Error('setmark() not supported') 213 | 214 | def getmark(self, id): 215 | raise Error('no marks') 216 | 217 | def getmarkers(self): 218 | return None 219 | 220 | def tell(self): 221 | return self._nframeswritten 222 | 223 | def writeframesraw(self, data): 224 | if not isinstance(data, (bytes, bytearray)): 225 | data = memoryview(data).cast('B') 226 | self._ensure_header_written(len(data)) 227 | #FIXME: Who cares?! 228 | nframes = len(data) // (self._sampwidth * self._nchannels) 229 | #FIXME: Remove this stupid feature 230 | if self._convert: 231 | data = self._convert(data) 232 | #FIXME: Handle this for ADPCM 233 | if self._sampwidth != 1 and sys.byteorder == 'big': 234 | data = audioop.byteswap(data, self._sampwidth) 235 | self._file.write(data) 236 | self._datawritten += len(data) 237 | self._nframeswritten = self._nframeswritten + nframes 238 | 239 | def writeframes(self, data): 240 | self.writeframesraw(data) 241 | if self._datalength != self._datawritten: 242 | self._patchheader() 243 | 244 | def close(self): 245 | try: 246 | if self._file: 247 | self._ensure_header_written(0) 248 | if self._datalength != self._datawritten: 249 | self._patchheader() 250 | self._file.flush() 251 | finally: 252 | self._file = None 253 | file = self._i_opened_the_file 254 | if file: 255 | self._i_opened_the_file = None 256 | file.close() 257 | 258 | # 259 | # Internal methods. 260 | # 261 | 262 | def _ensure_header_written(self, datasize): 263 | if not self._headerwritten: 264 | if not self._nchannels: 265 | raise Error('# channels not specified') 266 | if not self._format: 267 | raise Error('format not specified') 268 | if not self._sampwidth: 269 | raise Error('sample width not specified') 270 | if not self._framerate: 271 | raise Error('sampling rate not specified') 272 | self._write_header(datasize) 273 | 274 | def _write_header(self, initlength): 275 | assert not self._headerwritten 276 | self._file.write(b'RIFF') 277 | if not self._nframes: 278 | self._nframes = initlength // (self._nchannels * self._sampwidth) 279 | self._datalength = self._nframes * self._nchannels * self._sampwidth 280 | try: 281 | self._form_length_pos = self._file.tell() 282 | except (AttributeError, OSError): 283 | self._form_length_pos = None 284 | self._extra = 0 285 | if self._format == WAVE_FORMAT_PCM: 286 | bits_per_sample = self._sampwidth * 8 287 | block_align = self._sampwidth * self._nchannels 288 | bytes_per_sec = self._nchannels * self._framerate * self._sampwidth 289 | self._extra = 0 290 | elif self._format == WAVE_FORMAT_XBOX_ADPCM or self._format == WAVE_FORMAT_IMA_ADPCM: 291 | bits_per_sample = 4 292 | block_align = 0x24 * self._nchannels # No idea where the 0x24 comes from tbh.. 293 | bytes_per_sec = self._framerate * block_align // 64 294 | self._extra = 4 295 | else: 296 | raise Error('format not supported') 297 | self._file.write(struct.pack('> ffs(mask) 15 | 16 | def SetMasked(storage, mask, value): 17 | value = value << ffs(mask) 18 | return (storage & ~mask) | (value & mask) 19 | 20 | -------------------------------------------------------------------------------- /xboxpy/interface/__init__.py: -------------------------------------------------------------------------------- 1 | import os 2 | import sys 3 | 4 | from . import api 5 | 6 | # Helper to find the target 7 | 8 | def get_xbox_address(default_port, default_host = "localhost"): 9 | try: 10 | XBOX = os.environ['XBOX'] 11 | except: 12 | XBOX = "" 13 | 14 | pt = XBOX.partition(":") 15 | HOST = pt[0].strip() 16 | if len(HOST) == 0: 17 | HOST = default_host 18 | print("Using default host '" + HOST + "'") 19 | PORT = 0 20 | if (pt[2] != ""): 21 | try: 22 | PORT = int(pt[2]) 23 | if PORT < 0: 24 | print("Negative port number not allowed: '" + pt[2] + "'") 25 | PORT = 0 26 | elif PORT > 0xFFFF: 27 | print("Port number to high: '" + pt[2] + "'") 28 | PORT = 0 29 | except: 30 | print("Unable to parse port: '" + pt[2] + "'") 31 | if PORT == 0: 32 | PORT = default_port 33 | print("Using default port " + str(PORT)) 34 | return (HOST, PORT) 35 | 36 | try: 37 | used_interface = os.environ['XBOX_IF'].strip().lower() 38 | except: 39 | used_interface = None 40 | 41 | if used_interface == None or used_interface == 'xbdm': 42 | print("Using XBDM interface") 43 | from . import if_xbdm 44 | elif used_interface == 'gdb': 45 | print("Using gdb interface") 46 | from . import if_gdb 47 | elif used_interface == 'nxdk-rdt': 48 | print("Using nxdk-rdt interface") 49 | from . import if_nxdk_rdt 50 | elif used_interface == 'none': 51 | print("Warning: 'none' interface does not allow remote access") 52 | else: 53 | print("Unknown interface '" + used_interface + "'") 54 | sys.exit(1) 55 | -------------------------------------------------------------------------------- /xboxpy/interface/api.py: -------------------------------------------------------------------------------- 1 | def read(address, size): 2 | assert(False) 3 | def write(address, data): 4 | assert(False) 5 | def call(address, stack, registers=None): 6 | assert(False) 7 | -------------------------------------------------------------------------------- /xboxpy/interface/dbg_pb2.py: -------------------------------------------------------------------------------- 1 | # Generated by the protocol buffer compiler. DO NOT EDIT! 2 | # source: dbg.proto 3 | 4 | import sys 5 | _b=sys.version_info[0]<3 and (lambda x:x) or (lambda x:x.encode('latin1')) 6 | from google.protobuf import descriptor as _descriptor 7 | from google.protobuf import message as _message 8 | from google.protobuf import reflection as _reflection 9 | from google.protobuf import symbol_database as _symbol_database 10 | from google.protobuf import descriptor_pb2 11 | # @@protoc_insertion_point(imports) 12 | 13 | _sym_db = _symbol_database.Default() 14 | 15 | 16 | 17 | 18 | DESCRIPTOR = _descriptor.FileDescriptor( 19 | name='dbg.proto', 20 | package='dbg', 21 | syntax='proto2', 22 | serialized_pb=_b('\n\tdbg.proto\x12\x03\x64\x62g\"\x1d\n\x07SysInfo\x12\x12\n\ntick_count\x18\x01 \x02(\x03\"\x8d\x02\n\x07Request\x12\x1f\n\x04type\x18\x01 \x02(\x0e\x32\x11.dbg.Request.Type\x12\x0b\n\x03msg\x18\x02 \x01(\t\x12\x0f\n\x07\x61\x64\x64ress\x18\x03 \x01(\r\x12\x0c\n\x04size\x18\x04 \x01(\r\x12\x0c\n\x04\x64\x61ta\x18\x05 \x01(\x0c\"\xa6\x01\n\x04Type\x12\x0b\n\x07SYSINFO\x10\x00\x12\n\n\x06REBOOT\x10\x01\x12\n\n\x06MALLOC\x10\x02\x12\x08\n\x04\x46REE\x10\x03\x12\x0c\n\x08MEM_READ\x10\x04\x12\r\n\tMEM_WRITE\x10\x05\x12\x0f\n\x0b\x44\x45\x42UG_PRINT\x10\x06\x12\x15\n\x11SHOW_DEBUG_SCREEN\x10\x07\x12\x15\n\x11SHOW_FRONT_SCREEN\x10\x08\x12\x08\n\x04\x43\x41LL\x10\t\x12\t\n\x05\x43OUNT\x10\n\"\xc7\x01\n\x08Response\x12 \n\x04type\x18\x01 \x02(\x0e\x32\x12.dbg.Response.Type\x12\x0b\n\x03msg\x18\x02 \x01(\t\x12\x1a\n\x04info\x18\x03 \x01(\x0b\x32\x0c.dbg.SysInfo\x12\x0f\n\x07\x61\x64\x64ress\x18\x04 \x01(\r\x12\x0c\n\x04size\x18\x05 \x01(\r\x12\x0c\n\x04\x64\x61ta\x18\x06 \x01(\x0c\"C\n\x04Type\x12\x06\n\x02OK\x10\x00\x12\x15\n\x11\x45RROR_UNSUPPORTED\x10\x01\x12\x1c\n\x18\x45RROR_INCOMPLETE_REQUEST\x10\x02') 23 | ) 24 | 25 | 26 | 27 | _REQUEST_TYPE = _descriptor.EnumDescriptor( 28 | name='Type', 29 | full_name='dbg.Request.Type', 30 | filename=None, 31 | file=DESCRIPTOR, 32 | values=[ 33 | _descriptor.EnumValueDescriptor( 34 | name='SYSINFO', index=0, number=0, 35 | options=None, 36 | type=None), 37 | _descriptor.EnumValueDescriptor( 38 | name='REBOOT', index=1, number=1, 39 | options=None, 40 | type=None), 41 | _descriptor.EnumValueDescriptor( 42 | name='MALLOC', index=2, number=2, 43 | options=None, 44 | type=None), 45 | _descriptor.EnumValueDescriptor( 46 | name='FREE', index=3, number=3, 47 | options=None, 48 | type=None), 49 | _descriptor.EnumValueDescriptor( 50 | name='MEM_READ', index=4, number=4, 51 | options=None, 52 | type=None), 53 | _descriptor.EnumValueDescriptor( 54 | name='MEM_WRITE', index=5, number=5, 55 | options=None, 56 | type=None), 57 | _descriptor.EnumValueDescriptor( 58 | name='DEBUG_PRINT', index=6, number=6, 59 | options=None, 60 | type=None), 61 | _descriptor.EnumValueDescriptor( 62 | name='SHOW_DEBUG_SCREEN', index=7, number=7, 63 | options=None, 64 | type=None), 65 | _descriptor.EnumValueDescriptor( 66 | name='SHOW_FRONT_SCREEN', index=8, number=8, 67 | options=None, 68 | type=None), 69 | _descriptor.EnumValueDescriptor( 70 | name='CALL', index=9, number=9, 71 | options=None, 72 | type=None), 73 | _descriptor.EnumValueDescriptor( 74 | name='COUNT', index=10, number=10, 75 | options=None, 76 | type=None), 77 | ], 78 | containing_type=None, 79 | options=None, 80 | serialized_start=153, 81 | serialized_end=319, 82 | ) 83 | _sym_db.RegisterEnumDescriptor(_REQUEST_TYPE) 84 | 85 | _RESPONSE_TYPE = _descriptor.EnumDescriptor( 86 | name='Type', 87 | full_name='dbg.Response.Type', 88 | filename=None, 89 | file=DESCRIPTOR, 90 | values=[ 91 | _descriptor.EnumValueDescriptor( 92 | name='OK', index=0, number=0, 93 | options=None, 94 | type=None), 95 | _descriptor.EnumValueDescriptor( 96 | name='ERROR_UNSUPPORTED', index=1, number=1, 97 | options=None, 98 | type=None), 99 | _descriptor.EnumValueDescriptor( 100 | name='ERROR_INCOMPLETE_REQUEST', index=2, number=2, 101 | options=None, 102 | type=None), 103 | ], 104 | containing_type=None, 105 | options=None, 106 | serialized_start=454, 107 | serialized_end=521, 108 | ) 109 | _sym_db.RegisterEnumDescriptor(_RESPONSE_TYPE) 110 | 111 | 112 | _SYSINFO = _descriptor.Descriptor( 113 | name='SysInfo', 114 | full_name='dbg.SysInfo', 115 | filename=None, 116 | file=DESCRIPTOR, 117 | containing_type=None, 118 | fields=[ 119 | _descriptor.FieldDescriptor( 120 | name='tick_count', full_name='dbg.SysInfo.tick_count', index=0, 121 | number=1, type=3, cpp_type=2, label=2, 122 | has_default_value=False, default_value=0, 123 | message_type=None, enum_type=None, containing_type=None, 124 | is_extension=False, extension_scope=None, 125 | options=None, file=DESCRIPTOR), 126 | ], 127 | extensions=[ 128 | ], 129 | nested_types=[], 130 | enum_types=[ 131 | ], 132 | options=None, 133 | is_extendable=False, 134 | syntax='proto2', 135 | extension_ranges=[], 136 | oneofs=[ 137 | ], 138 | serialized_start=18, 139 | serialized_end=47, 140 | ) 141 | 142 | 143 | _REQUEST = _descriptor.Descriptor( 144 | name='Request', 145 | full_name='dbg.Request', 146 | filename=None, 147 | file=DESCRIPTOR, 148 | containing_type=None, 149 | fields=[ 150 | _descriptor.FieldDescriptor( 151 | name='type', full_name='dbg.Request.type', index=0, 152 | number=1, type=14, cpp_type=8, label=2, 153 | has_default_value=False, default_value=0, 154 | message_type=None, enum_type=None, containing_type=None, 155 | is_extension=False, extension_scope=None, 156 | options=None, file=DESCRIPTOR), 157 | _descriptor.FieldDescriptor( 158 | name='msg', full_name='dbg.Request.msg', index=1, 159 | number=2, type=9, cpp_type=9, label=1, 160 | has_default_value=False, default_value=_b("").decode('utf-8'), 161 | message_type=None, enum_type=None, containing_type=None, 162 | is_extension=False, extension_scope=None, 163 | options=None, file=DESCRIPTOR), 164 | _descriptor.FieldDescriptor( 165 | name='address', full_name='dbg.Request.address', index=2, 166 | number=3, type=13, cpp_type=3, label=1, 167 | has_default_value=False, default_value=0, 168 | message_type=None, enum_type=None, containing_type=None, 169 | is_extension=False, extension_scope=None, 170 | options=None, file=DESCRIPTOR), 171 | _descriptor.FieldDescriptor( 172 | name='size', full_name='dbg.Request.size', index=3, 173 | number=4, type=13, cpp_type=3, label=1, 174 | has_default_value=False, default_value=0, 175 | message_type=None, enum_type=None, containing_type=None, 176 | is_extension=False, extension_scope=None, 177 | options=None, file=DESCRIPTOR), 178 | _descriptor.FieldDescriptor( 179 | name='data', full_name='dbg.Request.data', index=4, 180 | number=5, type=12, cpp_type=9, label=1, 181 | has_default_value=False, default_value=_b(""), 182 | message_type=None, enum_type=None, containing_type=None, 183 | is_extension=False, extension_scope=None, 184 | options=None, file=DESCRIPTOR), 185 | ], 186 | extensions=[ 187 | ], 188 | nested_types=[], 189 | enum_types=[ 190 | _REQUEST_TYPE, 191 | ], 192 | options=None, 193 | is_extendable=False, 194 | syntax='proto2', 195 | extension_ranges=[], 196 | oneofs=[ 197 | ], 198 | serialized_start=50, 199 | serialized_end=319, 200 | ) 201 | 202 | 203 | _RESPONSE = _descriptor.Descriptor( 204 | name='Response', 205 | full_name='dbg.Response', 206 | filename=None, 207 | file=DESCRIPTOR, 208 | containing_type=None, 209 | fields=[ 210 | _descriptor.FieldDescriptor( 211 | name='type', full_name='dbg.Response.type', index=0, 212 | number=1, type=14, cpp_type=8, label=2, 213 | has_default_value=False, default_value=0, 214 | message_type=None, enum_type=None, containing_type=None, 215 | is_extension=False, extension_scope=None, 216 | options=None, file=DESCRIPTOR), 217 | _descriptor.FieldDescriptor( 218 | name='msg', full_name='dbg.Response.msg', index=1, 219 | number=2, type=9, cpp_type=9, label=1, 220 | has_default_value=False, default_value=_b("").decode('utf-8'), 221 | message_type=None, enum_type=None, containing_type=None, 222 | is_extension=False, extension_scope=None, 223 | options=None, file=DESCRIPTOR), 224 | _descriptor.FieldDescriptor( 225 | name='info', full_name='dbg.Response.info', index=2, 226 | number=3, type=11, cpp_type=10, label=1, 227 | has_default_value=False, default_value=None, 228 | message_type=None, enum_type=None, containing_type=None, 229 | is_extension=False, extension_scope=None, 230 | options=None, file=DESCRIPTOR), 231 | _descriptor.FieldDescriptor( 232 | name='address', full_name='dbg.Response.address', index=3, 233 | number=4, type=13, cpp_type=3, label=1, 234 | has_default_value=False, default_value=0, 235 | message_type=None, enum_type=None, containing_type=None, 236 | is_extension=False, extension_scope=None, 237 | options=None, file=DESCRIPTOR), 238 | _descriptor.FieldDescriptor( 239 | name='size', full_name='dbg.Response.size', index=4, 240 | number=5, type=13, cpp_type=3, label=1, 241 | has_default_value=False, default_value=0, 242 | message_type=None, enum_type=None, containing_type=None, 243 | is_extension=False, extension_scope=None, 244 | options=None, file=DESCRIPTOR), 245 | _descriptor.FieldDescriptor( 246 | name='data', full_name='dbg.Response.data', index=5, 247 | number=6, type=12, cpp_type=9, label=1, 248 | has_default_value=False, default_value=_b(""), 249 | message_type=None, enum_type=None, containing_type=None, 250 | is_extension=False, extension_scope=None, 251 | options=None, file=DESCRIPTOR), 252 | ], 253 | extensions=[ 254 | ], 255 | nested_types=[], 256 | enum_types=[ 257 | _RESPONSE_TYPE, 258 | ], 259 | options=None, 260 | is_extendable=False, 261 | syntax='proto2', 262 | extension_ranges=[], 263 | oneofs=[ 264 | ], 265 | serialized_start=322, 266 | serialized_end=521, 267 | ) 268 | 269 | _REQUEST.fields_by_name['type'].enum_type = _REQUEST_TYPE 270 | _REQUEST_TYPE.containing_type = _REQUEST 271 | _RESPONSE.fields_by_name['type'].enum_type = _RESPONSE_TYPE 272 | _RESPONSE.fields_by_name['info'].message_type = _SYSINFO 273 | _RESPONSE_TYPE.containing_type = _RESPONSE 274 | DESCRIPTOR.message_types_by_name['SysInfo'] = _SYSINFO 275 | DESCRIPTOR.message_types_by_name['Request'] = _REQUEST 276 | DESCRIPTOR.message_types_by_name['Response'] = _RESPONSE 277 | _sym_db.RegisterFileDescriptor(DESCRIPTOR) 278 | 279 | SysInfo = _reflection.GeneratedProtocolMessageType('SysInfo', (_message.Message,), dict( 280 | DESCRIPTOR = _SYSINFO, 281 | __module__ = 'dbg_pb2' 282 | # @@protoc_insertion_point(class_scope:dbg.SysInfo) 283 | )) 284 | _sym_db.RegisterMessage(SysInfo) 285 | 286 | Request = _reflection.GeneratedProtocolMessageType('Request', (_message.Message,), dict( 287 | DESCRIPTOR = _REQUEST, 288 | __module__ = 'dbg_pb2' 289 | # @@protoc_insertion_point(class_scope:dbg.Request) 290 | )) 291 | _sym_db.RegisterMessage(Request) 292 | 293 | Response = _reflection.GeneratedProtocolMessageType('Response', (_message.Message,), dict( 294 | DESCRIPTOR = _RESPONSE, 295 | __module__ = 'dbg_pb2' 296 | # @@protoc_insertion_point(class_scope:dbg.Response) 297 | )) 298 | _sym_db.RegisterMessage(Response) 299 | 300 | 301 | # @@protoc_insertion_point(module_scope) 302 | -------------------------------------------------------------------------------- /xboxpy/interface/if_gdb.py: -------------------------------------------------------------------------------- 1 | from . import api 2 | import gdb 3 | 4 | inf = gdb.selected_inferior() 5 | 6 | def read(address, size, physical): 7 | if physical: 8 | adddress |= 0x80000000 9 | return bytes(inf.read_memory(address, size)) 10 | 11 | def write(address, data, physical): 12 | if physical: 13 | adddress |= 0x80000000 14 | value = bytes(data) 15 | inf.write_memory (address, value) 16 | 17 | def call(address, stack, registers=None): 18 | assert(False) 19 | # Some magic with `call` using: 20 | # gdb.execute (command [, from_tty [, to_string]]) 21 | # Maybe? 22 | out_registers = {} 23 | out_registers['eax'] = 0 24 | return out_registers 25 | 26 | api.read = read 27 | api.write = write 28 | -------------------------------------------------------------------------------- /xboxpy/interface/if_nxdk_rdt.py: -------------------------------------------------------------------------------- 1 | from . import api 2 | from . import get_xbox_address 3 | import socket 4 | from .dbg_pb2 import * 5 | import struct 6 | 7 | (HOST, PORT) = get_xbox_address(9269) 8 | 9 | rdt = socket.create_connection((HOST, PORT), 5) 10 | 11 | def _send_simple_request(req, buffer_size=256): 12 | """Send a simple request, expect success""" 13 | rdt.send(req.SerializeToString()) 14 | res = Response() 15 | res.ParseFromString(rdt.recv(buffer_size)) 16 | if res.type != Response.OK: 17 | raise XboxError(res.msg) 18 | return res 19 | 20 | def read(address, size, physical): 21 | # nxdk-rdt is very buggy.. it needs all data in one network packet 22 | # in order to make this work, we split large reads into chunks 23 | N = 256 24 | if size > N: 25 | chunk = read(address, N, physical) # Do read 26 | return chunk + read(address + N, size - N, physical) # Process rest of data 27 | if physical: 28 | adddress |= 0x80000000 29 | req = Request() 30 | req.type = Request.MEM_READ 31 | req.size = size 32 | req.address = address 33 | res = _send_simple_request(req, size + 256) 34 | return res.data 35 | 36 | def write(address, data, physical): 37 | # nxdk-rdt is very buggy.. it needs all data in one network packet 38 | # in order to make this work, we split large writes into chunks 39 | N = 256 40 | if len(data) > N: 41 | write(address, data[0:N], physical) # Do write 42 | return write(address + N, data[N:], physical) # Process rest of data 43 | if physical: 44 | adddress |= 0x80000000 45 | req = Request() 46 | req.type = Request.MEM_WRITE 47 | req.data = bytes(data) 48 | req.address = address 49 | res = _send_simple_request(req) 50 | 51 | def call(address, stack=None): 52 | req = Request() 53 | req.type = Request.CALL 54 | req.address = address 55 | if stack is not None: 56 | req.data = stack 57 | res = _send_simple_request(req) 58 | out_registers = {} 59 | out_registers['eax'] = struct.unpack_from(" 0) 70 | res += xbdm.recv(remaining) 71 | return (status, bytes(res)) 72 | 73 | elif status == 204: 74 | return (status, str(res, encoding='ascii')) 75 | 76 | else: 77 | print("Unknown status: " + str(status)) 78 | print("from response: " + str(res)) 79 | #FIXME: Read remaining buffer?! 80 | 81 | return (status, res) 82 | 83 | def xbdm_command(cmd, data=None, length=None): 84 | #FIXME: If type is already in bytes we just send it binary! 85 | #print("Running '" + cmd + "'") 86 | xbdm.sendall(bytes(cmd + "\r\n", encoding='ascii')) 87 | #print("Sent") 88 | status, lines = xbdm_parse_response(length) 89 | 90 | # Respond with requested data 91 | if status == 204: 92 | xbdm.sendall(data) 93 | status, lines = xbdm_parse_response() 94 | 95 | #print("Done") 96 | return status, lines 97 | 98 | import re 99 | 100 | def xbdm_parse_keys(string): 101 | 102 | #FIXME: Rewrite this function to work without `re` 103 | 104 | result = dict(re.findall(r'(\S+)=(".*?"|\S+)', string)) 105 | 106 | # Clean up datatypes 107 | for key in result: 108 | if result[key][0:2] == '0x': 109 | result[key] = int(result[key][2:], 16) 110 | elif result[key][0] == '\"' and result[key][-1] == '\"': 111 | #FIXME: Unescape strings? 112 | result[key] = result[key].strip('"') 113 | else: 114 | assert(False) 115 | 116 | # There might be keys without values (tags), so we add those now 117 | tags = re.findall(r'(\S+)(?![^=])', string) 118 | for tag in tags: 119 | if tag not in result: 120 | result[tag] = True 121 | 122 | return result 123 | 124 | def GetModules(): 125 | modulesList = [] 126 | status, lines = xbdm_command("modules") 127 | 128 | for line in lines: 129 | module = xbdm_parse_keys(line) 130 | modulesList.append(module) 131 | 132 | print(modulesList) 133 | return modulesList 134 | 135 | def GetMem(addr, length): 136 | if length == 0: 137 | return bytes([]) 138 | if False: 139 | cmd = "getmem addr=0x" + format(addr, 'X') + " length=0x" + format(length, 'X') 140 | status, lines = xbdm_command(cmd) 141 | data = bytearray() 142 | for line in lines: 143 | line = str(line, encoding='ascii').strip() 144 | for i in range(0, len(line) // 2): 145 | byte = line[i*2+0:i*2+2] 146 | if '?' in byte: 147 | print("Oops?!") 148 | byte = '00' 149 | data.append(int(byte,16)) 150 | assert(len(data) == length) 151 | else: 152 | cmd = "getmem2 addr=0x" + format(addr, 'X') + " length=0x" + format(length, 'X') 153 | status, data = xbdm_command(cmd, length=length) 154 | return bytes(data) 155 | 156 | def SetMem(addr, data): 157 | value = bytes(data) 158 | 159 | # Unfortunately XBDM has a maximum line-buffer of about ~500 symbols. 160 | # We'll be conservative and only allow 200 bytes (400 hex digits). 161 | # That leaves ~100 bytes for the actual command. 162 | max_len = 200 163 | if len(value) > max_len: 164 | SetMem(addr, value[0:max_len]) 165 | SetMem(addr + max_len, value[max_len:]) 166 | else: 167 | cmd="setmem addr=0x" + format(addr, 'X') + " data=" 168 | for i in range(0, len(value)): 169 | cmd += format(value[i], '02X') 170 | xbdm_command(cmd) 171 | 172 | 173 | def delay_retry(reason): 174 | print(reason + ". Retrying in " + str(int(1000 * connection_timeout)) + " ms") 175 | time.sleep(connection_timeout) 176 | print("Retrying now") 177 | 178 | def connect(): 179 | global xbdm 180 | # Repeat until we are fully connected 181 | while True: 182 | # Wait until we get a connection 183 | while True: 184 | if xbdm != None: 185 | xbdm.close() 186 | try: 187 | xbdm = socket.create_connection((HOST, PORT), timeout=connection_timeout) 188 | break 189 | except socket.timeout: 190 | print("Connection timeout. Retrying") 191 | except socket.gaierror as err: 192 | sys.exit("Connection error: '" + str(err) + "'") 193 | except ConnectionRefusedError: 194 | delay_retry("Connection refused") 195 | except: 196 | sys.exit("Unknown connection error") 197 | # Get login message 198 | try: 199 | status, data = xbdm_parse_response() 200 | if status == None: 201 | raise 202 | if status != 201: 203 | delay_retry("Bad status " + str(status) + ", expected 201") 204 | continue 205 | except: 206 | delay_retry("Could not get expected response") 207 | continue 208 | # Leave the loop, we are connected! 209 | break 210 | 211 | print("Connecting to '" + HOST + "' (Port " + str(PORT) + ")") 212 | connect() 213 | 214 | 215 | # Used during bootstrap only! 216 | 217 | def read1(address, size, physical): 218 | assert(physical == False); 219 | return GetMem(address, size) 220 | def write1(address, data, physical): 221 | assert(physical == False); 222 | return SetMem(address, data) 223 | 224 | api.read = read1 225 | api.write = write1 226 | 227 | # Hack some functions so we have better read/write access 228 | # See xbdm-hack.md for more information 229 | 230 | hacked = False 231 | if True: 232 | 233 | from xboxpy.pe import * 234 | 235 | modules=GetModules() 236 | xbdm_module = [module for module in modules if module['name'] == "xbdm.dll"][0] 237 | xbdm_base = xbdm_module['base'] 238 | DmResumeThread_addr = resolve_export(35, image_base=xbdm_base) 239 | 240 | hack = "0F20C05025FFFFFEFF0F22C08B5424088B1A8B4A048B4208E2028A03E203668B03E2028B03E2028803E203668903E2028903E21F6089250000018089C129CC89E78D720CF3A4FFD38B25000001808944241C61894208580F22C0B80000DB02C2040090909090" 241 | xbdm_command("setmem addr=0x" + format(DmResumeThread_addr, 'X') + " data=" + hack) 242 | 243 | #hack_bank = DmResumeThread_addr + (len(hack) // 2) # Put communication base behind the hack code [pretty shitty..] 244 | hack_bank = xbdm_base # Overwrite xbdm PE header 245 | #hack_bank = 0xd0032FC0 # Works on console ? 246 | #hack_bank = 0xD004E000 - 12 # Top of stack - works in XQEMU?! 247 | 248 | hacked = True 249 | print("Hack installed, bank at 0x" + format(hack_bank, '08X')) 250 | 251 | def xbdm_hack(address, operation, data=0): 252 | SetMem(hack_bank, struct.pack("> 22) * 4 28 | #print("PDE at 0x" + format(pde_addr, '08X')) 29 | pde = read_u32(pde_addr) # Get PDE entry 30 | #print("PDE mapping to 0x" + format(pde & 0xFFFFF000, '08X')) 31 | if (pde & 1) == 0: 32 | print("PDE not valid?!") 33 | return 34 | if (pde & (1 << 7)) == 0: # Only if not large pages (0 = 4kiB, 1 = 4MiB) 35 | pte_base = 0xC0000000 # Hardcoded PTE 36 | pte_addr = pte_base + (virtual_address >> 12) * 4 # FIXME: `& 0x3FF` ? 37 | #print("PTE at 0x" + format(pte_addr, '08X')) 38 | pte = read_u32(pte_addr) # Get PTE entry 39 | was_mapped = (pte & 1 == 1) 40 | #if (was_mapped) == 0: 41 | # print("PDE was not valid?!") 42 | if mapped: 43 | pte |= 0x00000001 44 | else: 45 | pte &= 0xFFFFFFFE 46 | 47 | # Hack to identity map physical pages 48 | if (virtual_address & 0x80000000) != 0: 49 | pte = (virtual_address & 0x7FFFF000) | (pte & 0xFFF) 50 | 51 | #print("PTE mapping to 0x" + format(pte & 0xFFFFF000, '08X')) 52 | write_u32(pte_addr, pte) 53 | return was_mapped 54 | -------------------------------------------------------------------------------- /xboxpy/nv2a.py: -------------------------------------------------------------------------------- 1 | from . import memory 2 | 3 | def read_u8(address): 4 | return memory.read_u8(0xFD000000 + address) 5 | def read_u16(address): 6 | return memory.read_u16(0xFD000000 + address) 7 | def read_u32(address): 8 | return memory.read_u32(0xFD000000 + address) 9 | 10 | def write_u8(address, value): 11 | memory.write_u8(0xFD000000 + address, value) 12 | def write_u16(address, value): 13 | memory.write_u16(0xFD000000 + address, value) 14 | def write_u32(address, value): 15 | memory.write_u32(0xFD000000 + address, value) 16 | 17 | def ReadCRTC(i): 18 | write_u8(0x6013D4, i) 19 | return read_u8(0x6013D5) 20 | 21 | #FIXME: Swizzler ported from XQEMU.. does not work - WHY?! 22 | 23 | def GenerateSwizzleMask(size): 24 | bit = 1 25 | mask_bit = 1 26 | x = 0 27 | y = 0 28 | z = 0 29 | done = False 30 | while not done: 31 | done = True 32 | if bit < size[0]: 33 | x |= mask_bit 34 | mask_bit <<= 1 35 | done = False 36 | if bit < size[1]: 37 | y |= mask_bit 38 | mask_bit <<= 1 39 | done = False 40 | if bit < size[2]: 41 | z |= mask_bit 42 | mask_bit <<= 1 43 | done = False 44 | bit <<= 1 45 | assert(x ^ y ^ z == (mask_bit - 1)) 46 | return (x, y, z) 47 | 48 | # This fills a pattern with a value if your value has bits abcd and your 49 | # pattern is 11010100100 this will return: 0a0b0c00d00 50 | def _FillPattern(pattern, value): 51 | result = 0 52 | bit = 1 53 | while value > 0: 54 | if (pattern & bit): 55 | result |= bit if (value & 1) else 0 56 | value >>= 1 57 | bit <<= 1 58 | return result 59 | 60 | def GetSwizzledOffset(offset, mask, bits_per_pixel): 61 | assert(bits_per_pixel % 8 == 0) 62 | new_offset = _FillPattern(mask[0], offset[0]) 63 | new_offset |= _FillPattern(mask[1], offset[1]) 64 | new_offset |= _FillPattern(mask[2], offset[2]) 65 | return (bits_per_pixel // 8) * new_offset 66 | 67 | def Swizzle(): 68 | assert(False) 69 | 70 | def Unswizzle(data, bits_per_pixel, size, pitch): 71 | assert(bits_per_pixel % 8 == 0) 72 | bytes_per_pixel = bits_per_pixel // 8 73 | 74 | if len(size) == 2: 75 | size = (size[0], size[1], 1) 76 | pitch = (pitch, 0) 77 | elif len(size) == 3: 78 | assert(len(pitch) == 2) 79 | else: 80 | assert(False) # Unknown size 81 | 82 | data = bytes(data) 83 | unswizzled = bytearray([0] * len(data)) 84 | 85 | #print(size) 86 | mask = GenerateSwizzleMask(size) 87 | #print(bin(mask[0]).rjust(34)) 88 | #print(bin(mask[1]).rjust(34)) 89 | #print(bin(mask[2]).rjust(34)) 90 | 91 | for z in range(0, size[2]): 92 | for y in range(0, size[1]): 93 | for x in range(0, size[0]): 94 | src = GetSwizzledOffset((x, y, z), mask, bits_per_pixel) 95 | dst = z * pitch[1] + y * pitch[0] + x * bytes_per_pixel 96 | 97 | for i in range(0, bytes_per_pixel): 98 | b = data[src+i] 99 | unswizzled[dst+i] = b 100 | #unswizzled[dst:dst + bytes_per_pixel] = data[src:src + bytes_per_pixel] 101 | 102 | return bytes(unswizzled) 103 | -------------------------------------------------------------------------------- /xboxpy/ohci.py: -------------------------------------------------------------------------------- 1 | # Original File & Design: Thomas Frei 2 | # Modifications for xbox openxdk by bkenwright (bkenwright@xbdev.net) 3 | # Ported to python by Jannik Vogel 4 | 5 | eds = [0] * (176 + 0x100 + 0x100) # __u32 6 | EDA = 0 # u32 7 | ED = 0 # s_Endpointdescripor* 8 | found = 0 # u8 9 | 10 | Debug = True 11 | 12 | def DisableInterrupts(): 13 | pass #FIXME `cli` 14 | 15 | def EnableInterrupts(): 16 | pass #FIXME `sti` 17 | 18 | class ohci_roothub_regs_property(object): 19 | def __init__(self): 20 | self._base = None 21 | 22 | MAX_ROOT_PORTS = 15 23 | 24 | a = u32_property(0) 25 | b = u32_property(4) 26 | status = u32_property(8) 27 | portstatus = [u32_property(12 + i * 4) for i in range(0, MAX_ROOT_PORTS)] 28 | 29 | class OHCI(object): 30 | def __init__(self): 31 | self._base = None 32 | 33 | def u32_property(offset, array_size): 34 | getter = lambda self: read_u32(base + offset) 35 | setter = lambda self, value: write_u32(self._base + offset, value) 36 | return property(getter, setter) 37 | 38 | # hcca 39 | NUM_INTS = 32 40 | int_table = u16_property(0, NUM_INTS) 41 | frame_no = u16_property(NUM_INTS * 4 + 0) 42 | pad1 = u16_property(NUM_INTS * 4 + 2) 43 | done_head = u32_property(NUM_INTS * 4 + 4) 44 | reserved_for_hc = u8_property(NUM_INTS * 4 + 8, 116) 45 | 46 | # regs 47 | #/* control and status registers */ 48 | revision = u32_property(0) 49 | control = u32_property(4) 50 | cmdstatus = u32_property(8) 51 | intrstatus = u32_property(12) 52 | intrenable = u32_property(16) 53 | intrdisable = u32_property(20) 54 | #/* memory pointers */ 55 | hcca = u32_property(24) 56 | ed_periodcurrent = u32_property(28) 57 | ed_controlhead = u32_property(32) 58 | ed_controlcurrent = u32_property(36) 59 | ed_bulkhead = u32_property(40) 60 | ed_bulkcurrent = u32_property(44) 61 | donehead = u32_property(48) 62 | #/* frame counters */ 63 | fminterval = u32_property(52) 64 | fmremaining = u32_property(56) 65 | fmnumber = u32_property(60) 66 | periodicstart = u32_property(64) 67 | lsthresh = u32_property(68) 68 | #/* Root hub ports */ 69 | roothub = ohci_roothub_regs_property(72) #FIXME: Base!!! 70 | 71 | def FindOHC(ohci, regbase): 72 | 73 | DisableInterrupts() 74 | 75 | xmemset(ohci, 0, sizeof(ohci_t)) 76 | 77 | ohci.regs = regbase #(ohci_regs*)regbase 78 | 79 | 80 | # This is where we align or hcca so its 256 byte aligned 81 | hcca = malloc(0x10000 + 0x100) 82 | memset(hcca, 0x00, 0x10000 + 0x100) 83 | ohci.hcca = (hcca + 0x100) & 0xFFFFFF00 84 | 85 | ke.MmLockUnlockBufferPages(hcca, 0x10000 + 0x100, 0) 86 | 87 | ke.MmLockUnlockBufferPages(eds, 0x100, 0) 88 | 89 | # Just make sure our memory for ED's is aligned 90 | EDA = eds 91 | EDA += 0x100 92 | EDA &= 0xFFFFFF00 # e.g was 0x0002ED20, but is now 0x0002EE00, so its 93 | # 16 bit memory aligned 94 | 95 | ED = EDA #(s_Endpointdescripor*)EDA 96 | 97 | realEDA = ke.MmGetPhysicalAddress(EDA) 98 | 99 | 100 | __u32 * pHCCA = (__u32*)ohci.hcca 101 | for i in range(0, 32): 102 | pHCCA[i] = realEDA # EDA 103 | 104 | pHCCA[32] = 0 105 | pHCCA[33] = 0 106 | 107 | for i in range(0, 5): 108 | ED[i].Format = AUTOIO | 0x402000 # AUTOIO 0x1800L 109 | ED[i].NextED = realEDA + (16 * (i + 1)) # EDA + (16*(i+1)) 110 | ED[i].Headptr = 0 111 | ED[i].Tailptr = 0 112 | 113 | ED[0].Format = 0x4000 # Should really explain whats happening 114 | ED[0].NextED = 0 # here, ED[0] is going to be the bulkhead descriptor. 115 | ED[1].NextED = 0 # It is setup, but never used - as you'll always see 116 | ED[4].NextED = 0 # ED[1] being set with values in this demo code. 117 | # As ED's are all 16bit aligned this way. 118 | 119 | # init the hardware 120 | 121 | # what state is the hardware in at the moment? (usually reset state) 122 | state = ohci.regs.control & 0xC0 123 | if state == 0x00: # in reset: a cold boot 124 | ohci.regs.roothub.a = 0x10000204 125 | ohci.regs.roothub.b = 0x00020002 126 | ohci.regs.fminterval = (4096 << 16)| 11999 127 | ohci.regs.control = ((ohci.regs.control) & (~0xC0)) | 0x80 128 | else if state == 0x80: # operational already 129 | pass 130 | else if state == 0x40 or state == 0xC0: # resume or suspend 131 | ohci.regs.control = ((ohci.regs.control) & (~0xC0)) | 0x40 132 | time.sleep(50000) 133 | 134 | fminterval = ohci.regs.fminterval # stash interval 135 | ohci.regs.cmdstatus = 1 # host controller reset 136 | 137 | time.sleep(20) # should only take 10us max to reset 138 | 139 | ohci.regs.fminterval = fminterval # restore our saved interval 140 | 141 | realhcca = ke.MmGetPhysicalAddress(ohci.hcca) 142 | 143 | if False: 144 | ohci.regs.hcca = (__u32)ohci.hcca 145 | 146 | ohci.regs.hcca = realhcca 147 | 148 | if False: 149 | ohci.regs.intrdisable = ~0x0 # Not sure about this...but turn off any interrupts? 150 | 151 | ohci.regs.intrenable = 0xC000007B # set HcInterruptEnable 152 | 153 | 154 | ohci.regs.control = 0x00000080 # HC operational 155 | 156 | time.sleep(50) 157 | 158 | if Debug: 159 | # HCFS - usb state (00,01,10,11.reset,resume,operational,suspend) 160 | # bits 6 and 7 in the control register 161 | print("USBOPERATIONAL : " + ("yes" if % (((ohci.regs.control >> 6) & 0x3) == 0x2) else "no")) 162 | print("USBOPERATIONAL : 0x%02X" % ((ohci.regs.control >> 6) & 0x3)) 163 | 164 | ohci.regs.fminterval |= 0x27780000 # set FSLargestDataPacket 165 | 166 | 167 | # set HcPeriodicStart to 10% of HcFmInterval 168 | ohci.regs.periodicstart = (ohci.regs.fminterval & 0xFFFF) / 10 169 | 170 | 171 | # Global power on 172 | ohci.regs.roothub.status = 0x10000 173 | 174 | tt = ohci.regs.intrstatus # clear interrupts 175 | ohci.regs.intrstatus = tt 176 | 177 | 178 | if Debug: 179 | DebugFile( ohci) 180 | 181 | if False: 182 | ohci.regs.ed_bulkhead = EDA # BulkHead 183 | 184 | if False: 185 | __u32 realEDA = ke.MmGetPhysicalAddress(EDA) 186 | ohci.regs.ed_bulkhead = realEDA 187 | 188 | # link ED's in Queue 189 | if False: 190 | ohci.regs.ed_controlhead = EDA + 16 # ControlHead 191 | ohci.regs.ed_controlhead = realEDA + 16 192 | 193 | 194 | # NDP 195 | NDP = ohci.regs.roothub.a & 0xFF 196 | 197 | 198 | # disable all devices 199 | for i in range(0, 4): 200 | ohci.regs.roothub.portstatus[i] = 0x11 201 | 202 | return NDP 203 | 204 | def FindDev(ohci_t * ohci, Port): 205 | Speed=0 206 | 207 | 208 | s_USB_Devicedescriptor DD 209 | s_USB_Devicedescriptor * pDD = &DD 210 | xmemset(pDD, 0, sizeof(DD)) 211 | 212 | s_Transferdescriptor * TD 213 | 214 | GetDescr = malloc(8) 215 | write_u32(GetDescr + 0, 0x01000680) 216 | write_u32(GetDescr + 4, 0x00080000) 217 | 218 | WG = GetDescr 219 | ke.MmLockUnlockBufferPages(WG, 0x8, 0) 220 | DDA = pDD 221 | ke.MmLockUnlockBufferPages(DDA, 0x32, 0) 222 | 223 | TD = (s_Transferdescriptor*)(((__u32*)ED)+20)# Same as saying TD = EDA+80) 224 | TDA = EDA + 80 225 | 226 | realTDA = ke.MmGetPhysicalAddress(TDA) 227 | 228 | TD[0].Format = 0xE20050C0 # Get DeviceDescriptor 229 | TD[0].Buffer = ke.MmGetPhysicalAddress(WG) 230 | if False: 231 | TD[0].NextTD = TDA + 16 232 | TD[0].NextTD = realTDA + 16 233 | TD[0].BufferEnd = ke.MmGetPhysicalAddress(WG + 7) 234 | 235 | TD[1].Format = 0xE31050C1 # Receive first 8 bytes of DeviceDescriptor 236 | TD[1].Buffer = ke.MmGetPhysicalAddress(DDA) 237 | TD[1].NextTD = realTDA + 32 238 | TD[1].BufferEnd = ke.MmGetPhysicalAddress(DDA + 7) 239 | 240 | TD[2].Format = 0xE20050C2 # Queue END 241 | TD[2].Buffer = 0 242 | TD[2].NextTD = 0 243 | TD[2].BufferEnd = 0 244 | 245 | # Power on + Enable Ports 246 | ohci.regs.roothub.portstatus[Port] = 0x100 247 | time.sleep(2) 248 | 249 | if( (ohci.regs.roothub.portstatus[Port] & 1)== 0): 250 | return 0 # No device 251 | 252 | if( ohci.regs.roothub.portstatus[Port] & 0x200): # lowspeed device? 253 | Speed = 1 254 | else: 255 | Speed = 2 256 | 257 | if( ohci.regs.roothub.portstatus[Port] & 0x10000): # Port Power changed? 258 | ohci.regs.roothub.portstatus[Port] = 0x10000 # Port power Ack 259 | 260 | # Port Reset 261 | # We will try and do this 4 times 262 | ohci.regs.roothub.portstatus[Port] = 0x10 263 | time.sleep(40) 264 | for i in range(0, 4): 265 | if( (ohci.regs.roothub.portstatus[Port] & 0x10)== 0): 266 | break 267 | 268 | if Debug: 269 | print("\tport: %d, reset failed %d times" % (Port, i)) 270 | 271 | time.sleep(100) 272 | 273 | ohci.regs.roothub.portstatus[Port] = 0x100000 274 | 275 | if( (ohci.regs.roothub.portstatus[Port] & 7) != 3): # Port disabled? 276 | ohci.regs.roothub.portstatus[Port] = 2 277 | 278 | # Configure Endpointdescriptor 279 | if(Speed == 2): 280 | ED[1].Format &= 0xFFFFDFFF 281 | else: 282 | ED[1].Format |= 0x2000 283 | 284 | # determine MPS 285 | ED[1].Headptr = realTDA 286 | ED[1].Tailptr = realTDA + 32 287 | ED[1].Format &= 0xFFFFFF00 288 | 289 | # set CLF 290 | ohci.regs.cmdstatus |= 2 # CommandStatus 291 | 292 | ohci.regs.control = 0x90 # set CLE 293 | 294 | tt = ohci.regs.intrstatus # clear all Interruptflags 295 | ohci.regs.intrstatus = tt 296 | 297 | if Debug: 298 | DebugFile( ohci) 299 | # wait for execution 300 | print("waiting for execution\n") 301 | 302 | while True: 303 | ohci.regs.intrstatus = 0x4 # SOF 304 | if ((ohci.regs.intrstatus & 2)== 0): 305 | break 306 | 307 | time.sleep(10) 308 | 309 | # Errors? 310 | ohci_hcca *hcca = (ohci_hcca*)ohci.hcca # HCCA 311 | hcca.done_head &= 0xFFFFFFFE # DoneHead in HCCA 312 | 313 | if ((hcca.done_head >> 28)== 0): 314 | ED[1].Format &= 0xF800FFFF 315 | ED[1].Format |= DD.MaxPacketSize << 16 316 | found++ 317 | else: 318 | return 0 319 | 320 | if Debug: 321 | print("\nDescriptor.Length: 0x%x" % DD.Length) 322 | print("Descriptor.DescriptorType: 0x%02X" % DD.DescriptorType) 323 | print("Descriptor.USB: 0x%04X" % DD.USB) 324 | print("Descriptor.DeviceClass: 0x%04X" % DD.DeviceClass) 325 | print("Descriptor.DeviceSubClass: 0x%04X" % DD.DeviceSubClass) 326 | print("Descriptor.DeviceProtocol: 0x%04X" % DD.DeviceProtocol) 327 | print("Descriptor.MaxPacketSize: 0x%04X" % DD.MaxPacketSize) 328 | print("Descriptor.Vendor: 0x%04X" % DD.Vendor) 329 | print("Descriptor.ProductID: 0x%04X" % DD.ProductID) 330 | print("Descriptor.Manufacturer: 0x%04X" % DD.Manufacturer) 331 | print("Descriptor.ProductIndex: 0x%04X" % DD.ProductIndex) 332 | print("Descriptor.SerialNumber: 0x%04X" % DD.SerialNumber) 333 | print("Descriptor.ConfigNumber: 0x%04X" % DD.ConfigNumber) 334 | 335 | return Speed 336 | 337 | def ResetPort(ohci_t * ohci, Port): 338 | ohci.regs.roothub.portstatus[ Port*4 ] = 0x10 339 | time.sleep(40) 340 | 341 | ohci.regs.roothub.portstatus[ Port*4 ] = 0x100000 342 | 343 | return 0 344 | 345 | 346 | # Port = 8 bit [optional, value = 0] 347 | # AddrNew = 8 bit 348 | #FIXME: SetAddress 349 | def SetAddres(ohci_t * ohci, Port, AddrNew): 350 | WS = malloc(8) 351 | write_u32(WS + 0, (AddrNew << 16)| 0x00000500)# ??? 352 | write_u32(WS + 4, 0x00000000)# ??? 353 | 354 | if( Port == 0): 355 | Port = found 356 | 357 | ke.MmLockUnlockBufferPages(WS, 0x8, 0) 358 | 359 | # s_Transferdescriptor *TD 360 | 361 | if False: 362 | #FIXME: Dead code.. 363 | TD = malloc(4 * 0x1000) 364 | TD = (s_Transferdescriptor*)((TD + 0x100) & 0xFFFFFF00) 365 | xmemset(TD, 0, sizeof(s_Transferdescriptor)*4) 366 | 367 | if False: 368 | # FIXME: Dead code.. 369 | TD = (s_Transferdescriptor *)(((__u32 *)ED)+20) 370 | 371 | TD = (s_Transferdescriptor *)(EDA + 80) 372 | TDA = EDA + 80 373 | 374 | realTDA = ke.MmGetPhysicalAddress(TDA) 375 | 376 | TD[0].Format = 0xE20050C4 # Set Address 377 | TD[0].Buffer = ke.MmGetPhysicalAddress(WS) 378 | TD[0].NextTD = realTDA + 16 # (__u32)(&TD[1])//TDA + 16 379 | TD[0].BufferEnd = ke.MmGetPhysicalAddress(WS + 7) 380 | 381 | TD[1].Format = 0xE31050C5 # Receive Acknowledge 382 | TD[1].Buffer = 0 383 | TD[1].NextTD = realTDA + 32 # (__u32)(&TD[2])//TDA + 32 384 | TD[1].BufferEnd = 0 385 | 386 | TD[2].Format = 0xE20050C6 # End Queue 387 | TD[2].Buffer = 0 388 | TD[2].NextTD = 0 389 | TD[2].BufferEnd = 0 390 | 391 | if False: 392 | #FIXME: Dead code 393 | ED[1].Headptr = &TD[0] # TDA 394 | ED[1].Tailptr = &TD[2] # TDA + 32 395 | 396 | ED[1].Headptr = realTDA 397 | ED[1].Tailptr = realTDA + 32 398 | 399 | # set CLF 400 | ohci.regs.cmdstatus |= 2 # CommandStatus 401 | 402 | ohci.regs.control = 0x90 # set CLE 403 | 404 | ohci.regs.intrstatus = ohci.regs.intrstatus # clear all Interruptflags 405 | 406 | if Debug: 407 | DebugFile( ohci) 408 | # wait for execution 409 | print("waiting for execution\n") 410 | 411 | while((ohci.regs.intrstatus & 2) == 0): 412 | pass 413 | 414 | 415 | time.sleep(10) 416 | 417 | # ERRORS? 418 | ohci_hcca *hcca = (ohci_hcca*)ohci.hcca # HCCA 419 | hcca.done_head &= 0xFFFFFFFE # DoneHead in HCCA 420 | 421 | if( (hcca.done_head >> 28)!=0) 422 | return 1 423 | 424 | ED[1].Format &= 0xFFFFFF00 425 | ED[1].Format += AddrNew 426 | 427 | return 0 428 | 429 | 430 | def SetConfigur(ohci_t * ohci, __u8 Addr, __u8 Config): 431 | WC = malloc(8) 432 | write_u32(WC + 0, (Config << 16)| 0x00000900) 433 | write_u32(WC + 4, 0x00000000) 434 | 435 | s_Transferdescriptor *TD 436 | 437 | ke.MmLockUnlockBufferPages(WC, 0x8, 0) 438 | 439 | TD = (s_Transferdescriptor *)(((__u32 *)ED)+20) 440 | TDA = EDA + 80 441 | 442 | realTDA = ke.MmGetPhysicalAddress(TDA) 443 | 444 | TD[0].Format = 0xE20050C7 # Set Configuration 445 | TD[0].Buffer = ke.MmGetPhysicalAddress(WC) 446 | TD[0].NextTD = realTDA + 16 # TDA + 16 447 | TD[0].BufferEnd = ke.MmGetPhysicalAddress(WC + 7) 448 | 449 | TD[1].Format = 0xE30050C8 450 | TD[1].Buffer = 0 451 | TD[1].NextTD = 0 452 | TD[1].BufferEnd = 0 453 | 454 | ED[1].Headptr = realTDA # TDA 455 | ED[1].Tailptr = realTDA + 16 # TDA + 16 456 | ED[1].Format &= 0xFFFFFF00 457 | ED[1].Format += Addr 458 | 459 | 460 | # set CLF 461 | ohci.regs.cmdstatus |= 2 # CommandStatus 462 | 463 | ohci.regs.control = 0x90 # set CLE 464 | 465 | ohci.regs.intrstatus = ohci.regs.intrstatus # clear all Interruptflags 466 | 467 | if Debug: 468 | # wait for execution 469 | print("waiting for execution\n") 470 | 471 | while((ohci.regs.intrstatus & 2)== 0): 472 | pass 473 | 474 | time.sleep(10) 475 | 476 | 477 | # ERRORS? 478 | if False: 479 | #FIXME: Dead code 480 | ohci_hcca *hcca = (ohci_hcca*)ohci.regs.hcca # HCCA 481 | ohci_hcca *hcca = (ohci_hcca*)ohci.hcca 482 | hcca.done_head &= 0xFFFFFFFE # DoneHead in HCCA 483 | 484 | if((hcca.done_head >> 28)!= 0): 485 | return 1 486 | 487 | return 0 488 | 489 | 490 | def GetDesc(ohci_t * ohci, __u8 Addr, __u8 DescrType, __u8 Index, __u8 Count, __u8 *DBuffer): 491 | 492 | GetDescr = malloc(8) 493 | write_u32(GetDescr + 0, (DescrType << 24) | (Index << 16) | 0x00000680) 494 | write_u32(GetDescr + 4, 0x00000000) 495 | 496 | WG = GetDescr 497 | 498 | DA = malloc(256)#__u8 Descriptors[256] 499 | 500 | ke.MmLockUnlockBufferPages( WG, 0x8, 0) 501 | ke.MmLockUnlockBufferPages( DA, 256, 0) 502 | 503 | realDA = ke.MmGetPhysicalAddress(DA) 504 | 505 | lCount = Count # u8 506 | 507 | s_Transferdescriptor *TD 508 | 509 | TD = (s_Transferdescriptor *)(((__u32 *)ED)+ 20) 510 | TDA = EDA + 80 511 | 512 | realTDA = ke.MmGetPhysicalAddress(TDA) 513 | 514 | TD[0].Format = 0xE20050CA # Get Descriptor 515 | TD[0].Buffer = ke.MmGetPhysicalAddress(WG) 516 | TD[0].NextTD = realTDA + 16 # TDA + 16 517 | TD[0].BufferEnd = ke.MmGetPhysicalAddress(WG+7) 518 | 519 | TD[1].Format = 0xE31450CB # Receive Start of Descriptor 520 | TD[1].Buffer = realDA # DA 521 | TD[1].NextTD = realTDA + 32 # TDA+32 522 | TD[1].BufferEnd = realDA + 7 # DA + 7 523 | 524 | 525 | TD[2].Format = 0xE21450CC # Receive Rest of Descriptor 526 | TD[2].Buffer = realDA + 8 # DA + 8 527 | TD[2].NextTD = realTDA + 48 # TDA + 48 528 | TD[2].BufferEnd = 0 529 | 530 | TD[3].Format = 0xE30050CD # Queue END 531 | TD[3].Buffer = 0 532 | TD[3].NextTD = 0 533 | TD[3].BufferEnd = 0 534 | 535 | write_u32(GetDescr + 4, lCount << 16) 536 | ED[1].Headptr = realTDA # TDA 537 | ED[1].Tailptr = realTDA + 32 # TDA + 32 538 | ED[1].Format &= 0xFFFFFF00 539 | ED[1].Format += Addr 540 | 541 | if( DescrType == 3) 542 | tmp = read_u32(GetDescr + 4) 543 | write_u32(GetDescr + 4, tmp | 0x0409) 544 | TD[1].BufferEnd = realDA + lCount - 1 # DA + lCount - 1 545 | 546 | 547 | # set CLF 548 | ohci.regs.cmdstatus |= 2 # CommandStatus 549 | 550 | ohci.regs.control = 0x90 # set CLE 551 | 552 | ohci.regs.intrstatus = ohci.regs.intrstatus # clear all Interruptflags 553 | 554 | 555 | if Debug: 556 | # wait for execution 557 | print("waiting for execution\n") 558 | 559 | while ((ohci.regs.intrstatus & 2)== 0): 560 | pass 561 | 562 | time.sleep(10) 563 | 564 | # ERRORS? 565 | if False: 566 | #FIXME: Dead code 567 | ohci_hcca *hcca = (ohci_hcca*)ohci.regs.hcca # HCCA 568 | else: 569 | ohci_hcca *hcca = (ohci_hcca*)ohci.hcca 570 | hcca.done_head &= 0xFFFFFFFE # DoneHead in HCCA 571 | 572 | if ((hcca.done_head >> 28)!= 0): 573 | if Debug: 574 | print("\nError Occured\n") 575 | return 1 576 | 577 | ED[1].Headptr = realTDA + 48 # TDA + 48 578 | ED[1].Tailptr = realTDA + 64 # TDA + 64 579 | 580 | 581 | #FIXME: This is always False?! 582 | if((DescrType == 3)&& (lCount < read_u8(DA + 0))&& False): 583 | # set CLF 584 | ohci.regs.cmdstatus |= 2 # CommandStatus 585 | 586 | ohci.regs.control = 0x90 # set CLE 587 | 588 | ohci.regs.intrstatus = ohci.regs.intrstatus # clear all Interruptflags 589 | 590 | if Debug: 591 | # wait for execution 592 | print("waiting for execution") 593 | 594 | while((ohci.regs.intrstatus & 2)== 0): 595 | pass 596 | 597 | time.sleep(10) 598 | 599 | # ERRORS? 600 | if False: 601 | #FIXME: Dead code?! 602 | ohci_hcca *hcca = (ohci_hcca*)ohci.regs.hcca # HCCA 603 | else: 604 | ohci_hcca *hcca = (ohci_hcca*)ohci.hcca 605 | 606 | hcca.done_head &= 0xFFFFFFFE # DoneHead in HCCA 607 | 608 | if ((hcca.done_head>>28)!= 0): 609 | return 1 610 | 611 | 612 | if (DescrType == 2): 613 | lCount = min(lCount, Descriptors[2]) 614 | else: 615 | lCount = min(lCount, Descriptors[0]) 616 | 617 | 618 | 619 | if False: 620 | print("Descriptors:") 621 | for i in range(0, lCount) 622 | print("%X " % Descriptors[i]) 623 | print("") 624 | 625 | 626 | time.sleep(10) 627 | 628 | xmemcpy( DBuffer, Descriptors, lCount) 629 | 630 | return 0 631 | 632 | 633 | 634 | # These function are nothing more than debug functions - they are used 635 | # to save the register, or descriptor values back to file, so we can check 636 | # that all is okay and ticking away as we want :) 637 | 638 | def DebugFile(ohci_t * ohci): 639 | 640 | print(" revision 0x%08X" % ohci.regs.revision) 641 | print(" control 0x%08X" % ohci.regs.control) 642 | print(" cmdstatus 0x%08X" % ohci.regs.cmdstatus) 643 | print(" intrstatus 0x%08X" % ohci.regs.intrstatus) 644 | print(" intrenable 0x%08X" % ohci.regs.intrenable) 645 | print(" intrdisable 0x%08X" % ohci.regs.intrdisable) 646 | print(" ed_periodcurrent 0x%08X" % ohci.regs.ed_periodcurrent) 647 | print(" ed_controlhead 0x%08X" % ohci.regs.ed_controlhead) 648 | print(" ed_controlcurrent 0x%08X" % ohci.regs.ed_controlcurrent) 649 | print(" ed_bulkhead 0x%08X" % ohci.regs.ed_bulkhead) 650 | print(" ed_bulkcurrent 0x%08X" % ohci.regs.ed_bulkcurrent) 651 | print(" donehead 0x%08X" % ohci.regs.donehead) 652 | print(" fminterval 0x%08X" % ohci.regs.fminterval) 653 | print(" fmremaining 0x%08X" % ohci.regs.fmremaining) 654 | print(" periodicstart 0x%08X" % ohci.regs.periodicstart) 655 | print(" lsthresh 0x%08X" % ohci.regs.lsthresh) 656 | print(" ohci_roothub_regs.a 0x%08X" % ohci.regs.roothub.a) 657 | print(" ohci_roothub_regs.b 0x%08X" % ohci.regs.roothub.b) 658 | print(" ohci_roothub_regs.status 0x%08X" % ohci.regs.roothub.status) 659 | 660 | 661 | print(" ohci_roothub_regs.portstatus[0] 0x%08X" % ohci.regs.roothub.portstatus[0]) 662 | print(" ohci_roothub_regs.portstatus[1] 0x%08X" % ohci.regs.roothub.portstatus[1]) 663 | print(" ohci_roothub_regs.portstatus[2] 0x%08X" % ohci.regs.roothub.portstatus[2]) 664 | print(" ohci_roothub_regs.portstatus[3] 0x%08X" % ohci.regs.roothub.portstatus[3]) 665 | 666 | 667 | 668 | def DebugDescriptor( s_USB_Devicedescriptor * pDes): 669 | print("\n*Descriptor*\n") 670 | 671 | print("Descriptor.Length: 0x%x" % pDes.Length) 672 | print("Descriptor.DescriptorType: 0x%02X" % pDes.DescriptorType) 673 | print("Descriptor.USB: 0x%04X" % pDes.USB) 674 | print("Descriptor.DeviceClass: 0x%04X" % pDes.DeviceClass) 675 | print("Descriptor.DeviceSubClass: 0x%04X" % pDes.DeviceSubClass) 676 | print("Descriptor.DeviceProtocol: 0x%04X" % pDes.DeviceProtocol) 677 | print("Descriptor.MaxPacketSize: 0x%04X" % pDes.MaxPacketSize) 678 | 679 | print("Descriptor.Vendor: 0x%04X" % pDes.Vendor) 680 | print("Descriptor.ProductID: 0x%04X" % pDes.ProductID) 681 | print("Descriptor.Device: 0x%04X" % pDes.Device) 682 | 683 | print("Descriptor.Manufacturer: 0x%02X" % pDes.Manufacturer) 684 | print("Descriptor.ProductIndex: 0x%02X" % pDes.ProductIndex) 685 | print("Descriptor.SerialNumber: 0x%02X" % pDes.SerialNumber) 686 | print("Descriptor.ConfigNumber: 0x%02X" % pDes.ConfigNumber) 687 | 688 | 689 | def DebugConfigDescriptor( s_USB_Configurationdescriptor * pDes): 690 | print("\n+Configuration Descriptor+\n") 691 | 692 | print("ConfDesc.Length: 0x%02X" % pDes.Length) 693 | print("ConfDescDescriptorType: 0x%02X" % pDes.DescriptorType) 694 | print("ConfDesc.TotalLength: 0x%04X" % pDes.TotalLength) 695 | print("ConfDesc.NumberofInterfaces: 0x%02X" % pDes.NumberofInterfaces) 696 | 697 | print("ConfDesc.ConfigValue: 0x%02X" % pDes.ConfigValue) 698 | print("ConfDesc.Configuration: 0x%02X" % pDes.Configuration) 699 | print("ConfDesc.Attributes: 0x%02X" % pDes.Attributes) 700 | print("ConfDesc.MaxPower: 0x%02X" % pDes.MaxPower) 701 | 702 | 703 | def DebugInterfaceDescriptor( s_USB_Interfacedescriptor * pDes): 704 | print("\n#Interface Descriptor#\n") 705 | 706 | print("InterDesc.Length: 0x%02X" % pDes.Length) 707 | print("InterDesc.DescriptorType: 0x%02X" % pDes.DescriptorType) 708 | print("InterDesc.Interfacenumber: 0x%02X" % pDes.Interfacenumber) 709 | print("InterDesc.AlternateSetting: 0x%02X" % pDes.AlternateSetting) 710 | print("InterDesc.NumberofEndpoints: 0x%02X" % pDes.NumberofEndpoints) 711 | print("InterDesc.InterfaceClass: 0x%02X" % pDes.InterfaceClass) 712 | print("InterDesc.InterfaceSubClass: 0x%02X" % pDes.InterfaceSubClass) 713 | print("InterDesc.InterfaceProtocol: 0x%02X" % pDes.InterfaceProtocol) 714 | print("InterDesc.InterfaceIndex: 0x%02X" % pDes.InterfaceIndex) 715 | 716 | def DebugEndPointDescriptor( s_USB_Endpointdescriptor * pDes): 717 | print("\n#EndPointDescriptor Descriptor#\n") 718 | 719 | print("EndPointDes.Length: 0x%02X" % pDes.Length) 720 | print("EndPointDes.DescriptorType: 0x%02X" % pDes.DescriptorType) 721 | print("EndPointDes.EndpointAddress: 0x%02X" % pDes.EndpointAddress) 722 | print("EndPointDes.Attributes: 0x%02X" % pDes.Attributes) 723 | print("EndPointDes.MaxPacketSize: 0x%02X" % pDes.MaxPacketSize) 724 | print("EndPointDes.Interval: 0x%02X" % pDes.Interval) 725 | -------------------------------------------------------------------------------- /xboxpy/ohci_pad.py: -------------------------------------------------------------------------------- 1 | #/* File: pad.cpp */ 2 | #/* bkenwright@xbdev.net - www.xbdev.net */ 3 | 4 | Debug = True 5 | 6 | 7 | #// These couple of globals are defined in ohci.cpp - there our small group 8 | #// of descriptors which we use to communcate with the ohci-usb 9 | 10 | extern __u32 eds[176 + 0x100 + 0x100]; // ohci.cpp 11 | extern __u32 EDA; 12 | extern s_Endpointdescripor * ED; 13 | 14 | 15 | 16 | def OHCI_ED_GET_FA(s): 17 | return ((s) & 0x7f) 18 | OHCI_ED_ADDRMASK = 0x0000007f 19 | def OHCI_ED_SET_FA(s): 20 | return (s) 21 | def OHCI_ED_GET_EN(s): 22 | return (((s) >> 7) & 0xf) 23 | def OHCI_ED_SET_EN(s): 24 | return ((s) << 7) 25 | OHCI_ED_DIR_MASK = 0x00001800 26 | OHCI_ED_DIR_TD = 0x00000000 27 | OHCI_ED_DIR_OUT = 0x00000800 28 | OHCI_ED_DIR_IN = 0x00001000 29 | OHCI_ED_SPEED = 0x00002000 30 | OHCI_ED_SKIP = 0x00004000 31 | OHCI_ED_FORMAT_GEN = 0x00000000 32 | OHCI_ED_FORMAT_ISO = 0x00008000 33 | def OHCI_ED_GET_MAXP(s): 34 | return (((s) >> 16) & 0x07ff) 35 | def OHCI_ED_SET_MAXP(s) 36 | return ((s) << 16) 37 | OHCI_ED_MAXPMASK = (0x7ff << 16) 38 | 39 | OHCI_HALTED = 0x00000001 40 | OHCI_TOGGLECARRY = 0x00000002 41 | OHCI_HEADMASK = 0xfffffffc 42 | 43 | struct ohci_ed_t_ 44 | { 45 | __u32 ed_flags; 46 | __u32 ed_tailp; 47 | __u32 ed_headp; 48 | __u32 ed_nexted; 49 | }; 50 | 51 | 52 | OHCI_TD_R 0x00040000 #/* Buffer Rounding */ 53 | OHCI_TD_DP_MASK 0x00180000 #/* Direction / PID */ 54 | OHCI_TD_SETUP 0x00000000 55 | OHCI_TD_OUT 0x00080000 56 | OHCI_TD_IN 0x00100000 57 | def OHCI_TD_GET_DI(x): 58 | return (((x) >> 21) & 7) #/* Delay Interrupt */ 59 | def OHCI_TD_SET_DI(x): 60 | return ((x) << 21) 61 | OHCI_TD_NOINTR 0x00e00000 62 | OHCI_TD_INTR_MASK 0x00e00000 63 | OHCI_TD_TOGGLE_CARRY 0x00000000 64 | OHCI_TD_TOGGLE_0 0x02000000 65 | OHCI_TD_TOGGLE_1 0x03000000 66 | OHCI_TD_TOGGLE_MASK 0x03000000 67 | def OHCI_TD_GET_EC(x): 68 | return (((x) >> 26) & 3) #/* Error Count */ 69 | def OHCI_TD_GET_CC(x): 70 | return ((x) >> 28) #/* Condition Code */ 71 | OHCI_TD_NOCC = 0xf0000000 72 | 73 | struct ohci_td_t_ 74 | { 75 | __u32 td_flags; 76 | __u32 td_cbp; /* Current Buffer Pointer */ 77 | __u32 td_nexttd; /* Next TD */ 78 | __u32 td_be; /* Buffer End */ 79 | } ; 80 | 81 | 82 | OHCI_CTRL_CLE = (1 << 4) #/* control list enable */ 83 | OHCI_CTRL_BLE = (1 << 5) #/* bulk list enable */ 84 | OHCI_CTRL_HCFS = (3 << 6) #/* host controller functional state */ 85 | 86 | 87 | OHCI_CLF = (1 << 1) #/* control list filled */ 88 | OHCI_BLF = (1 << 2) #/* bulk list filled */ 89 | 90 | OHCI_CTRL_CLE = (1 << 4) #/* control list enable */ 91 | OHCI_CTRL_BLE = (1 << 5) #/* bulk list enable */ 92 | OHCI_CTRL_HCFS = (3 << 6) #/* host controller functional state */ 93 | 94 | 95 | OHCI_CLF = (1 << 1) #/* control list filled */ 96 | OHCI_BLF = (1 << 2) #/* bulk list filled */ 97 | 98 | 99 | #/******************************************************************************/ 100 | 101 | 102 | 103 | def usb_bulk_msg( usbd_device * dev, data): 104 | ohci_t * ohci = dev->p_ohci; 105 | 106 | 107 | size = len(data) 108 | 109 | _buffer = malloc(256) # FIXME: free later? 110 | xmemcpy(_buffer, data, size) 111 | 112 | ke.MmLockUnlockBufferPages(_buffer, 0x8, 0) 113 | real_pcmd = ke.MmGetPhysicalAddress(_buffer) 114 | 115 | #__u8 Descriptors[256] = {0}; 116 | Descriptors = malloc(256) # FIXME: free later? 117 | ke.MmLockUnlockBufferPages(Descriptors, 256, 0 ) 118 | real_pDescriptors = ke.MmGetPhysicalAddress(Descriptors) 119 | 120 | s_Transferdescriptor *TD; 121 | TD = (s_Transferdescriptor *)(((__u32 *)ED) + 20); 122 | __u32 TDA = EDA + 80; 123 | __u32 realTDA = ke.MmGetPhysicalAddress(TDA) 124 | 125 | 126 | #//////////////////////////////////////////////////////////////////////// 127 | #// --Transfer Descriptor TD-- 128 | #// Offset Field Size Value Desc 129 | #// 0 4 0..17 reserved 130 | #// 131 | #// 18 R - bufferRounding 132 | #// 19..20 DP - Direction/PID 133 | #// 00b SETUP 134 | #// 01b OUT 135 | #// 10b IN 136 | #// 11b reserved 137 | #// 21..23 DI - DelayInterrupt 138 | #// 24..25 T - DataToggle 139 | #// 26..27 EC - ErrorCount 140 | #// 28..31 CC - ConditionCode 141 | #// 4 4 CurrentBufferPoinnter (CBP) 142 | #// 8 4 0..3 Zero 143 | #// 4..31 Next TD (NextTD) 144 | #// 12 4 Buffer End (BE) 145 | #// 146 | #//(Total Size 16 bytes or 4 dwords) 147 | #// 148 | #//////////////////////////////////////////////////////////////////////// 149 | 150 | 151 | // 0xE20050CA 152 | 153 | __u32 td_format = 0; 154 | td_format |= OHCI_TD_OUT; // 0x00080000 e.g. 1<<19 155 | td_format |= OHCI_TD_TOGGLE_0; // 0x02000000 e.g. 2<<24 156 | /* I've set the data toggle sync here in 157 | * the TD by setting the MSB to 1 of Toggle Bits 158 | * so the ED toggle bit isn't used. 159 | */ 160 | td_format |= OHCI_TD_NOCC; // 0xf0000000 161 | /* Set our ConditionCode to no errors */ 162 | 163 | 164 | TD[0].Format = td_format; 165 | #//TD[0].Format = 0xE20050CA; // Our Bulk Msg! 166 | TD[0].Buffer = real_pcmd; 167 | TD[0].NextTD = realTDA + 16; 168 | TD[0].BufferEnd = real_pcmd + size - 1; 169 | 170 | 171 | td_format |= 0; 172 | td_format |= OHCI_TD_OUT; 173 | td_format |= OHCI_TD_TOGGLE_1; 174 | td_format |= OHCI_TD_NOCC; 175 | 176 | TD[1].Format = td_format; // Receive Start of Descriptor 177 | TD[1].Buffer = real_pDescriptors; 178 | TD[1].NextTD = realTDA + 32; 179 | TD[1].BufferEnd = real_pDescriptors + 7; 180 | 181 | 182 | td_format |= 0; 183 | td_format |= OHCI_TD_OUT; 184 | td_format |= OHCI_TD_TOGGLE_0; 185 | td_format |= OHCI_TD_NOCC; 186 | 187 | TD[3].Format = td_format; 188 | #//TD[1].Format = 0xE30050CB; // Queue END 189 | TD[3].Buffer = 0; 190 | TD[3].NextTD = 0; 191 | TD[3].BufferEnd = 0; 192 | 193 | # //////////////////////////////////////////////////////////////////////// 194 | #// --EndPoint Descriptor ED-- 195 | #// Offset Field Size Value Desc 196 | #// 0 4 Bitmap 197 | #// 0..6 FA - Function Address 198 | #// 199 | #// 7..10 EN - EndpointNumber 200 | #// 11..12 D - Direction 201 | #// 00b GetDir From TD 202 | #// 01b OUT 203 | #// 10b IN 204 | #// 11b GetDir From TD 205 | #// 13 S - Speed (Full Speed=0) 206 | #// 14 K - sKip 207 | #// 15 F - Format 208 | #// 0 - Control/Bulk/Int******** 209 | #// 1 - Isochronous 210 | #// 16..26 MPS MaximumPacketSize 211 | #// 27..31 reserved 212 | #// 4 4 Bitmap 213 | #// 0..3 Zero Aligment 214 | #// 4..31 TD Queue Tail Pointer (TailP) 215 | #// 8 4 Bitmap 216 | #// 0 H - Halted 217 | #// 1 C - toggleCarry 218 | #// 2..3 reserved 219 | #// 4..31 TD Queue Head Pointer (HeadP) 220 | #// 12 4 Bitmap 221 | #// 0..3 Zero Aligment 222 | #// 4..31 Next Endpoint Descriptor (NextED) 223 | #// 224 | #//(Total Size 16 bytes or 4 dwords) 225 | #// 226 | #//////////////////////////////////////////////////////////////////////// 227 | 228 | #//__u32 format = 0x00401801; // 2(OUT) or 82(IN)? 229 | #// 230 | 231 | __u32 ed_format = 0x00400000; 232 | ed_format |= OHCI_ED_DIR_OUT; // 0x00000800 233 | ed_format |= OHCI_ED_DIR_IN; // 0x00001000 234 | 235 | 236 | 237 | __u8 Addr = dev->address; 238 | __u8 EndAddr = 0x2; 239 | 240 | ED[0].Format = ed_format; 241 | 242 | ED[0].Headptr = realTDA; 243 | ED[0].Tailptr = realTDA + 16; 244 | ED[0].Format &= 0xFFFFFF00; // Clear old address 245 | ED[0].Format += Addr; // Insert new address 246 | 247 | ED[0].Format |= (EndAddr << 7 ); 248 | 249 | 250 | #// have to enable bulk tx 251 | #//ohci->regs->cmdstatus |= 2; // CommandStatus 252 | #//ohci->regs->control = 0x90; // set CLE 253 | 254 | ohci->regs->cmdstatus |= (1<<2); // BLF 255 | ohci->regs->control = OHCI_CTRL_BLE | (2<<6); // BLF 256 | ohci->regs->intrstatus = ohci->regs->intrstatus; // clear all Interruptflags 257 | 258 | 259 | #// wait for execution 260 | #//dbg("waiting for execution\n"); 261 | 262 | while( (ohci->regs->intrstatus & 2)== 0 ): 263 | pass 264 | 265 | xSleep(10) 266 | 267 | #// ERRORS? 268 | ohci_hcca *hcca = (ohci_hcca*)ohci->hcca #// HCCA 269 | hcca->done_head &= 0xfffffffe #// DoneHead in HCCA 270 | 271 | if( (hcca->done_head>>28) !=0 ): 272 | if Debug: 273 | print("\nError Occured\n") 274 | return 275 | 276 | //------------------------------------------------------------------ 277 | 278 | #// Where all done and finished now, so we set our ED's to our 279 | #// Queue END where they behave and wait. 280 | ED[0].Headptr = realTDA + 48 281 | ED[0].Tailptr = realTDA + 64 282 | 283 | if Debug: 284 | ErrorCount = OHCI_TD_GET_EC( TD[0].Format ) 285 | ConditionCode = OHCI_TD_GET_CC( TD[0].Format ) 286 | print("ErrorCount: " + str(ErrorCount)) 287 | print("ConditionCode: " + str(ConditionCode)) 288 | 289 | xSleep(10) 290 | 291 | #//xmemcpy( data, Descriptors, size); 292 | 293 | 294 | ED[0].Format = 0x00004000 295 | 296 | 297 | 298 | 299 | __u8 xbuffer[256] = {0}; 300 | 301 | def usb_bulk_msg_in( usbd_device * dev, size) 302 | { 303 | 304 | ohci_t * ohci = dev->p_ohci; 305 | 306 | xmemset(xbuffer, 0, 256 ); 307 | 308 | 309 | xMmLockUnlockBufferPages( (__u32)xbuffer, 0x250, 0); 310 | __u32 real_pbuffer = xMmGetPhysicalAddress( (__u32)xbuffer); 311 | 312 | 313 | s_Transferdescriptor *TD; 314 | TD = (s_Transferdescriptor *)(((__u32 *)ED) + 20); 315 | __u32 TDA = EDA + 80; 316 | __u32 realTDA = xMmGetPhysicalAddress( (__u32)TDA ); 317 | 318 | 319 | 320 | // 0xE20050CA 321 | 322 | __u32 td_format = 0; 323 | td_format |= OHCI_TD_IN; // 0x00100000 e.g. 1<<19 324 | td_format |= OHCI_TD_TOGGLE_0; // 0x02000000 e.g. 2<<24 325 | /* I've set the data toggle sync here in 326 | * the TD by setting the MSB to 1 of Toggle Bits 327 | * so the ED toggle bit isn't used. 328 | */ 329 | td_format |= OHCI_TD_NOCC; // 0xf0000000 330 | /* Set our ConditionCode to no errors */ 331 | 332 | TD[0].Format = td_format; // Our Bulk Msg! 333 | TD[0].Buffer = real_pbuffer; 334 | TD[0].NextTD = realTDA + 16; 335 | TD[0].BufferEnd = real_pbuffer + size - 1; 336 | 337 | 338 | if False: 339 | td_format |= 0; 340 | td_format |= OHCI_TD_IN; 341 | td_format |= OHCI_TD_TOGGLE_1; 342 | td_format |= OHCI_TD_NOCC; 343 | TD[1].Format = td_format; // Receive Start of Descriptor 344 | TD[1].Buffer = real_pbuffer; 345 | TD[1].NextTD = realTDA + 32; 346 | TD[1].BufferEnd = real_pbuffer + size - 1; 347 | 348 | td_format |= 0; 349 | td_format |= OHCI_TD_IN; 350 | td_format |= OHCI_TD_TOGGLE_1; 351 | td_format |= OHCI_TD_NOCC; 352 | TD[1].Format = td_format; // Queue END 353 | TD[1].Buffer = 0; 354 | TD[1].NextTD = 0; 355 | TD[1].BufferEnd = 0; 356 | 357 | 358 | 359 | __u32 ed_format = 0x00400000; 360 | ed_format |= OHCI_ED_DIR_OUT; // 0x00000800 361 | ed_format |= OHCI_ED_DIR_IN; // 0x00001000 362 | 363 | 364 | 365 | __u8 Addr = dev->address; 366 | __u8 EndAddr = 0x2; 367 | 368 | ED[0].Format = ed_format; 369 | 370 | ED[0].Headptr = realTDA; 371 | ED[0].Tailptr = realTDA + 16; 372 | ED[0].Format &= 0xFFFFFF00; // Clear old address 373 | ED[0].Format += Addr; // Insert new address 374 | 375 | ED[0].Format |= (EndAddr << 7 ); 376 | 377 | 378 | xSleep(20); 379 | 380 | #// have to enable bulk tx 381 | #//ohci->regs->cmdstatus |= 2; // CommandStatus 382 | #//ohci->regs->control = 0x90; // set CLE 383 | 384 | ohci->regs->cmdstatus |= (1<<2); // BLF 385 | ohci->regs->control = OHCI_CTRL_BLE | (2<<6); // BLF 386 | ohci->regs->intrstatus = ohci->regs->intrstatus; // clear all Interruptflags 387 | 388 | 389 | // wait for execution 390 | //dbg("waiting for execution\n"); 391 | do 392 | { 393 | //dbg("waiting for execution-in loop\n"); 394 | 395 | }while( (ohci->regs->intrstatus & 2)== 0 ); 396 | 397 | //dbg("waiting for execution-out loop\n"); 398 | 399 | ohci->regs->intrstatus = ohci->regs->intrstatus; 400 | 401 | xSleep(30); 402 | 403 | // ERRORS? 404 | ohci_hcca *hcca = (ohci_hcca*)ohci->hcca; // HCCA 405 | hcca->done_head &= 0xfffffffe; // DoneHead in HCCA 406 | 407 | if( (hcca->done_head>>28) !=0 ): 408 | if Debug: 409 | print("\nError Occured\n") 410 | return 411 | 412 | #FIXME: Why is this only happening for debug in original code?!?!?! looks like a bug 413 | #// Where all done and finished now, so we set our ED's to our 414 | #// Queue END where they behave and wait. 415 | ED[0].Headptr = realTDA + 48; 416 | ED[0].Tailptr = realTDA + 64; 417 | 418 | if Debug: 419 | #FIXME: Why is this only happening for debug ?!?!?! 420 | 421 | __u32 ErrorCount = OHCI_TD_GET_EC( TD[0].Format ); 422 | __u32 ConditionCode = OHCI_TD_GET_CC( TD[0].Format ); 423 | 424 | 425 | if( ErrorCount || ConditionCode ): 426 | print("ErrorCount: " + str(ErrorCount)) 427 | print("ConditionCode: " + str(ConditionCode)) 428 | 429 | #//xSleep(50); 430 | 431 | data = bytearray() 432 | xmemcpy( data, xbuffer, size); 433 | 434 | 435 | #//ED[0].Format = 0x00004000; 436 | 437 | return data 438 | -------------------------------------------------------------------------------- /xboxpy/pe.py: -------------------------------------------------------------------------------- 1 | from .memory import * 2 | 3 | def resolve_export(ordinal, image_base=0x80010000): 4 | #FIXME: If this is a string, look up its ordinal 5 | TempPtr = read_u32(image_base + 0x3C); 6 | TempPtr = read_u32(image_base + TempPtr + 0x78); 7 | ExportCount = read_u32(image_base + TempPtr + 0x14); 8 | ExportBase = image_base + read_u32(image_base + TempPtr + 0x1C); 9 | #FIXME: Read all exports at once and parse them locally 10 | 11 | #for i in range(0, ExportCount): 12 | # ordinal = i + 1 13 | # print("@" + str(ordinal) + ": 0x" + format(image_base + read_u32(ExportBase + i * 4), '08X')) 14 | 15 | index = (ordinal - 1) # Ordinal 16 | #assert(index < ExportCount) #FIXME: Off by one? 17 | return image_base + read_u32(ExportBase + index * 4) 18 | --------------------------------------------------------------------------------