<?xml version="1.0" encoding="utf-8"?><feed xmlns="http://www.w3.org/2005/Atom" xml:lang="en"><generator uri="https://jekyllrb.com/" version="4.4.1">Jekyll</generator><link href="https://netdex.org/feed.xml" rel="self" type="application/atom+xml" /><link href="https://netdex.org/" rel="alternate" type="text/html" hreflang="en" /><updated>2026-06-25T03:43:38+00:00</updated><id>https://netdex.org/feed.xml</id><title type="html">netdex</title><subtitle>Gordon Guan</subtitle><author><name>netdex</name></author><entry><title type="html">Overclocking Ryzen 5000, suffering edition™</title><link href="https://netdex.org/2022/11/05/overclock-ryzen-5000/" rel="alternate" type="text/html" title="Overclocking Ryzen 5000, suffering edition™" /><published>2022-11-05T00:00:00+00:00</published><updated>2022-11-05T00:00:00+00:00</updated><id>https://netdex.org/2022/11/05/overclock-ryzen-5000</id><content type="html" xml:base="https://netdex.org/2022/11/05/overclock-ryzen-5000/"><![CDATA[<p>Overclocking computer components has traditionally been a delicate balance of
power, thermals, voltage, and clock speed.</p>

<!--more-->

<p>The higher the clock speed, the more operations your processor can complete
within a given period of time. However, it takes a minimum amount of time for
the logic in your processor to be driven to a given logic level. If your clock
speed is too fast, you’ll start violating timing requirements and cause
instability. Increasing voltage reduces the amount of time it takes to settle
to a given logic level, subsequently increasing your maximum theoretical clock
speed. Unfortunately, it also increases power consumption. The cost of
electricity aside, the components on your board are only capable of delivering
power up to their specified limits. Before you reach that limit though, the
increase in power consumption forces you to mind your thermals. Not only to
avoid exceeding their maximum rated temperature, but also since silicon
performs worse at higher temperatures.</p>

<p>To make things worse, your processor dynamically scales its clock speed based
on load. This is especially important on battery power since hurtling along at
maximum clock speed is a waste of energy at idle. Reducing clock speed lets us
achieve the same result with less voltage, and thus less power. This means, for
every single clock speed our processor could be at, we need to ensure:</p>
<ul>
  <li>The voltage is sufficient such that logic levels settle fast enough</li>
  <li>The voltage is low enough such that it doesn’t use too much power</li>
  <li>The power is low enough such that the processor (and any power delivery) doesn’t get too hot</li>
  <li>Some other details I’ll leave out (voltage is low enough such that electro-migration doesn’t happen, some considerations related to the process node etc.)</li>
</ul>

<p>Of course, there are clock speeds our processor won’t be able to achieve since
at least one of the conditions will be violated. For example, there will most
certainly be a frequency where the voltage you’d need to drive the processor at
to achieve it would cause it to overheat or incur damage.</p>

<p>Something I didn’t go into detail is the importance of the cooling part, since
this lets us get a bit more headroom with thermals. If your components are
air-cooled, you’ll also need to make a trade-off between fan voltage, cooling
performance, and noise. I could write entire article just about cooling so I’ll
leave it at that.</p>

<h2 id="we-need-to-go-faster">We need to go faster</h2>
<p>Modern processors are capable of on-demand boosting, which allows them to
temporarily increase their clock speeds past their base specification depending
on load. Many workflows tend to be bursty in nature, such as browsing the web.
Boosting allows us to adapt to bursty workloads by temporarily increasing clock
speeds, improving perceivable latency amongst other things. Sometimes you could
make the argument that completing work faster allows the processor to return to
an idle state quicker</p>

<p>Why not stay at boost clocks indefinitely? We are abusing the fact that the
processor takes time to heat up due to thermal mass, so it’s possible to
achieve higher than normal clocks for a short period of time. There is no
equilibrium though - eventually the processor will get too hot to continue
running at boost frequencies, at which point it needs to decide between
lowering its frequency or returning to sand.</p>

<h2 id="precision-boost-overdrive">Precision Boost Overdrive</h2>
<p>The latest Ryzen processors from AMD have a feature named Precision Boost (and
Precision Boost 2), which balances several of the factors I mentioned above to
scale clock speeds on a per-core basis. If you’re interested in the details,
consider looking it up because how Precision Boost works has been covered to
death.</p>

<p>Precision Boost Overdrive (PBO) is a feature which allows you to overclock your
processor by tweaking the parameters used by Precision Boost. This differs from
traditional overclocking in which we typically set either the maximum boost or
base frequency to a fixed value, then tune voltage to an appropriate fixed value
or auto-offset. While this might be easier, we are leaving a lot on the table
for reasons I’ll briefly go over later. Here are the knobs and dials which PBO
gives us to tune the behavior of Precision Boost:</p>

<h3 id="limits">Limits</h3>
<ul>
  <li><strong>Package Power Tracking (PPT)</strong>: The maximum power that can be delivered to the CPU socket (watts).</li>
  <li><strong>Thermal Design Current (TDC)</strong>: The maximum current that can be delivered in sustained loads, when constrained by thermals (amps).</li>
  <li><strong>Electrical Design Current (EDC)</strong>: The maximum current that can be delivered in transient loads, when constrained by electrical characteristics (amps).</li>
</ul>

<p>PBO allows us to tweak the power and current limits applied to the CPU on a
global basis. Raising power limits allows cores to operate on higher clock
speeds by indirectly giving them more budget to be driven at higher voltages. Of
course, there’s a limit to how high we can push them, which is thermals again.
Not only of the CPU, but also the regulators and other components on the
motherboard.</p>

<h3 id="curve-optimizer">Curve Optimizer</h3>
<ul>
  <li><strong>Voltage-Frequency Curve:</strong>: A relation which specifies the minimum voltage
required to drive the processor at a given frequency.</li>
</ul>

<p>Curve optimizer allows us to apply an offset to the voltage-frequency curve.
This is similar to undervolting by applying a negative auto-offset, except curve
optimizer allows us to do it on a per-core basis. This is important because even
though they are on the same die, you will see that some cores require less
voltage to run at a certain frequency. By achieving the same frequency but with
less voltage, we unlock thermal headroom which allows us to remain at higher
frequencies for a longer period of time.</p>

<p>The downside is that it is notoriously difficult to ensure your configuration is
stable. With traditional overclocking, you could be fairly confident that your
overclock is stable by just running a torture test, since the voltage is fixed
at a point which should be sufficient for any frequency the CPU could possibly
encounter. This means that the maximum frequency is where we would most likely
see instability. Of course, this means our voltages are unnecessarily high for
lower frequencies, costing us precious thermal headroom and power consumption.
The situation is a bit better with auto-offset voltages, but the offset is
constrained by the worst core on the processor and terrible implementations by
motherboard vendors.</p>

<p>Since we are applying the offset to a <em>curve</em>, we effectively have to test every
frequency on every core in order to ensure system stability.</p>

<p><img src="/assets/images/posts/2022-11-05-overclock-ryzen-5000/vf1.svg" alt="v-f curve" />
For example, if the green line represents the minimum voltage required at a given
frequency for the core to be stable, and the red line is the current
voltage-frequency curve, we would see instability at lower frequencies (i.e.
when the core is idle!)</p>

<p>We can’t move the green line since it is a physical property of the core, but we
can move the red line around using curve optimizer by applying a voltage offset.</p>

<p><img src="/assets/images/posts/2022-11-05-overclock-ryzen-5000/vf2.svg" alt="v-f curve" /></p>

<p>There exist tools such as CoreCycler which helps exercise different parts of the
voltage-frequency curve on a per-core basis, to help with verifying stability.
After a bunch of tedious work, we can arrive at a set of core offsets which will
generally reduce the amount of power our CPU consumes, giving it more headroom.</p>

<p><img src="/assets/images/posts/2022-11-05-overclock-ryzen-5000/cotune.png" alt="co offsets" /></p>

<p>It’s worth mentioning that CoreCycler creates synthetic conditions which aren’t
realistic, which is why you’ll sometimes see instability with CoreCycler but no
issues under normal use. Sometimes you’ll even need to apply positive offsets,
which implies that the processor was unstable out of the box!</p>

<h3 id="scalar-multiplier-and-maximum-boost-offset">Scalar Multiplier and Maximum Boost Offset</h3>
<ul>
  <li><strong>Scalar Multiplier</strong>: A parameter affecting how long cores can remain at higher boost clocks.</li>
  <li><strong>Maximum Boost Offset</strong>: An offset applied to the maximum boost frequency the processor can achieve globally.</li>
</ul>

<h3 id="maximum-temperature">Maximum Temperature</h3>
<ul>
  <li><strong>Temperature</strong>: Measured via several sensors finely spread across the CPU die.</li>
</ul>

<h2 id="references">References</h2>
<p><a href="https://www.youtube.com/watch?v=NTkRuZ3uSO4">https://www.youtube.com/watch?v=NTkRuZ3uSO4</a><br />
<a href="https://skatterbencher.com/2021/08/18/skatterbencher-26-amd-ryzen-9-5900x-overclocked-to-5223-mhz">https://skatterbencher.com/2021/08/18/skatterbencher-26-amd-ryzen-9-5900x-overclocked-to-5223-mhz</a><br />
<a href="https://www.gamersnexus.net/guides/3491-explaining-precision-boost-overdrive-benchmarks-auto-oc">https://www.gamersnexus.net/guides/3491-explaining-precision-boost-overdrive-benchmarks-auto-oc</a></p>]]></content><author><name>netdex</name></author><summary type="html"><![CDATA[Overclocking computer components has traditionally been a delicate balance of power, thermals, voltage, and clock speed.]]></summary></entry><entry><title type="html">5GHz Access Point with Intel WiFi</title><link href="https://netdex.org/2022/07/20/iwlwifi-5g-ap/" rel="alternate" type="text/html" title="5GHz Access Point with Intel WiFi" /><published>2022-07-20T00:00:00+00:00</published><updated>2022-07-20T00:00:00+00:00</updated><id>https://netdex.org/2022/07/20/iwlwifi-5g-ap</id><content type="html" xml:base="https://netdex.org/2022/07/20/iwlwifi-5g-ap/"><![CDATA[<p>I have a thin client that I wanted to turn into a WiFi access point. “Consumer
APs are basically just really slow computers. Then, shouldn’t a moderately slow
computer be a really fast AP?” In theory, it checked out. Just slap
<a href="https://openwrt.org">OpenWrt</a> onto it or something of that ilk, and Bob’s your
uncle.</p>

<!--more-->

<p>My thin client has built-in WiFi, which simplifies things somewhat because there
aren’t any PCIe expansion slots to spare after shoving a second ethernet card
into it. The wireless adapter in question is an Intel Wireless-AC 9560 in M.2
form factor, boasting 2x2 802.11ac support – enabling high-throughput transfers
over the 5GHz band. On Linux, this adapter is controlled by the iwlwifi driver.</p>

<p><img src="https://i.imgur.com/Cz3hjsk.png" alt="" /></p>

<p>I proceeded to download the necessary drivers and firmware on my OpenWrt
instance, which was simple enough. Then, I created a 5GHz access point through
LuCI, pulled out my phone, and scanned for networks. However, no matter how long
I waited, nothing appeared. I couldn’t possibly know at that moment how much
time I’d waste trying to get this to work.</p>

<p>Looking at the logs, it seems to be failing in the DFS CAC stage.</p>

<!-- ![](https://i.imgur.com/rL0TP2A.png) -->

<div class="language-plaintext highlighter-rouge"><div class="highlight"><pre class="highlight"><code>Thu Jul 21 21:53:49 2022 daemon.notice hostapd: Remove interface 'wlan0'
Thu Jul 21 21:53:49 2022 daemon.notice hostapd: wlanO: interface state DISABLED-&gt;DISABLED
Thu Jul 21 21:53:49 2022 daemon.notice hostapd: wlanO: AP-DISABLED
Thu Jul 21 21:53:49 2022 daemon.notice hostapd: wlan0: CTRL-EVENT-TERMINATING
Thu Jul 21 21:53:49 2022 daemon.err hostapd: hostapd_free_hapd_data: Interface wlan0 wasn't started
Thu Jul 21 21:53:49 2022 daemon.notice hostapd: n180211: deinit ifname=wlan0 disabled_11b_rates=0
Thu Jul 21 21:53:49 2022 daemon.notice netifd: Wireless device 'radio0' is now down
Thu Jul 21 21:53:49 2022 daemon.notice hostapd: Configuration file: /var/run/hostapd-phy0.conf (phy wlan0) —&gt; new PHY
Thu Jul 21 21:53:49 2022 daemon.notice hostapd: wlanO: interface state UNINITIALIZED-&gt;COUNTRY_UPDATE
Thu Jul 21 21:53:49 2022 daemon.notice hostapd: wlanO: interface state COUNTRY_UPDATE-&gt;HT_SCAN
Thu Jul 21 21:53:50 2022 daemon.notice hostapd: wlan0: interface state HT_SCAN-&gt;DFS
Thu Jul 21 21:53:50 2022 daemon.notice hostapd: wlanO: DFS-CAC-START freq=5260 chan=52 sec_chan=1, width=0, seg0=54, seg1=0, cac_time=60s
Thu Jul 21 21:53:50 2022 daemon.err hostapd: DFS start_dfs_cac() failed, -1
Thu Jul 21 21:53:50 2022 daemon.err hostapd: Interface initialization failed
Thu Jul 21 21:53:50 2022 daemon.notice hostapd: wlanO: interface state DFS-DISABLED
Thu Jul 21 21:53:50 2022 daemon.notice hostapd: wlanO: AP-DISABLED
</code></pre></div></div>

<p><a href="https://www.asus.com/support/FAQ/1045936/">DFS</a> is a technology that allows
5GHz WiFi to coexist with certain radar technologies which share the same radio
spectra. It works by performing a Channel Availability Check (CAC) before
transmitting on any DFS channel. This check involves listening on the channel
for radar activity. Whether or not a channel is DFS or not depends on your
country’s local ordinance, formally referred to as <em>regulatory domain</em> (or
regdom for short). It seems that for whatever reason, DFS CAC doesn’t work with
this combination of wireless adapter and driver. I wouldn’t be surprised if it’s
not even implemented, because CAC is only used in AP mode. This network adapter
belongs in laptops, which never enter AP mode in normal operation.</p>

<p>Yeah, so DFS seems to be broken. And the channel is marked DFS because of the
regdom. Where did the regdom come from?</p>

<div class="language-plaintext highlighter-rouge"><div class="highlight"><pre class="highlight"><code>root@OpenWrt:~# iw reg get
global
country 00: DFS-UNSET
        (2402 - 2472 @ 40), (N/A, 20), (N/A)
        (2457 - 2482 @ 20), (N/A, 20), (N/A), AUTO-BW, PASSIVE-SCAN
        (2474 - 2494 @ 20), (N/A, 20), (N/A), NO-OFDM, PASSIVE-SCAN
        (5170 - 5250 @ 80), (N/A, 20), (N/A), AUTO-BW
        (5250 - 5330 @ 80), (N/A, 20), (0 ms), DFS, AUTO-BW, PASSIVE-SCAN
        (5490 - 5730 @ 160), (N/A, 20), (0 ms), DFS, PASSIVE-SCAN
        (5735 - 5835 @ 80), (N/A, 20), (N/A), PASSIVE-SCAN
        (57240 - 63720 @ 2160), (N/A, 0), (N/A)
...
</code></pre></div></div>

<p>The default regdom is “world”, which is an overly restrictive regdom that should
fly no matter what country you’re in. Let’s set it to somewhere that has some
5GHz channels that aren’t DFS,
<a href="https://en.wikipedia.org/wiki/List_of_WLAN_channels#5_GHz_(802.11a/h/j/n/ac/ax)">like India</a>.</p>

<div class="language-plaintext highlighter-rouge"><div class="highlight"><pre class="highlight"><code>root@OpenWrt:~# iw reg get
global
country IN: DFS-UNSET
        (2402 - 2472 @ 40), (N/A, 30), (N/A)
        (5150 - 5250 @ 80), (N/A, 30), (N/A)
        (5250 - 5350 @ 80), (N/A, 24), (N/A)
        (5470 - 5725 @ 160), (N/A, 24), (N/A)
        (5725 - 5875 @ 80), (N/A, 30), (N/A)

phy#0 (self-managed)
country 00: DFS-UNSET
        (2402 - 2437 @ 40), (6, 22), (N/A), AUTO-BW, NO-HT40MINUS, NO-80MHZ, NO-160MHZ
        (2422 - 2462 @ 40), (6, 22), (N/A), AUTO-BW, NO-80MHZ, NO-160MHZ
        (2447 - 2482 @ 40), (6, 22), (N/A), AUTO-BW, NO-HT40PLUS, NO-80MHZ, NO-160MHZ
        (5170 - 5190 @ 160), (6, 22), (N/A), NO-OUTDOOR, AUTO-BW, IR-CONCURRENT, NO-HT40MINUS, PASSIVE-SCAN
        (5190 - 5210 @ 160), (6, 22), (N/A), NO-OUTDOOR, AUTO-BW, IR-CONCURRENT, NO-HT40PLUS, PASSIVE-SCAN
        (5210 - 5230 @ 160), (6, 22), (N/A), NO-OUTDOOR, AUTO-BW, IR-CONCURRENT, NO-HT40MINUS, PASSIVE-SCAN
        (5230 - 5250 @ 160), (6, 22), (N/A), NO-OUTDOOR, AUTO-BW, IR-CONCURRENT, NO-HT40PLUS, PASSIVE-SCAN
        (5250 - 5270 @ 160), (6, 22), (0 ms), DFS, AUTO-BW, NO-HT40MINUS, PASSIVE-SCAN
        (5270 - 5290 @ 160), (6, 22), (0 ms), DFS, AUTO-BW, NO-HT40PLUS, PASSIVE-SCAN
        (5290 - 5310 @ 160), (6, 22), (0 ms), DFS, AUTO-BW, NO-HT40MINUS, PASSIVE-SCAN
        (5310 - 5330 @ 160), (6, 22), (0 ms), DFS, AUTO-BW, NO-HT40PLUS, PASSIVE-SCAN
        (5490 - 5510 @ 240), (6, 22), (0 ms), DFS, AUTO-BW, NO-HT40MINUS, PASSIVE-SCAN
        (5510 - 5530 @ 240), (6, 22), (0 ms), DFS, AUTO-BW, NO-HT40PLUS, PASSIVE-SCAN
        (5530 - 5550 @ 240), (6, 22), (0 ms), DFS, AUTO-BW, NO-HT40MINUS, PASSIVE-SCAN
        (5550 - 5570 @ 240), (6, 22), (0 ms), DFS, AUTO-BW, NO-HT40PLUS, PASSIVE-SCAN
        (5570 - 5590 @ 240), (6, 22), (0 ms), DFS, AUTO-BW, NO-HT40MINUS, PASSIVE-SCAN
        (5590 - 5610 @ 240), (6, 22), (0 ms), DFS, AUTO-BW, NO-HT40PLUS, PASSIVE-SCAN
        (5610 - 5630 @ 240), (6, 22), (0 ms), DFS, AUTO-BW, NO-HT40MINUS, PASSIVE-SCAN
        (5630 - 5650 @ 240), (6, 22), (0 ms), DFS, AUTO-BW, NO-HT40PLUS, PASSIVE-SCAN
        (5650 - 5670 @ 80), (6, 22), (0 ms), DFS, AUTO-BW, NO-HT40MINUS, NO-160MHZ, PASSIVE-SCAN
        (5670 - 5690 @ 80), (6, 22), (0 ms), DFS, AUTO-BW, NO-HT40PLUS, NO-160MHZ, PASSIVE-SCAN
        (5690 - 5710 @ 80), (6, 22), (0 ms), DFS, AUTO-BW, NO-HT40MINUS, NO-160MHZ, PASSIVE-SCAN
        (5710 - 5730 @ 80), (6, 22), (0 ms), DFS, AUTO-BW, NO-HT40PLUS, NO-160MHZ, PASSIVE-SCAN
        (5735 - 5755 @ 80), (6, 22), (N/A), AUTO-BW, IR-CONCURRENT, NO-HT40MINUS, NO-160MHZ, PASSIVE-SCAN
        (5755 - 5775 @ 80), (6, 22), (N/A), AUTO-BW, IR-CONCURRENT, NO-HT40PLUS, NO-160MHZ, PASSIVE-SCAN
        (5775 - 5795 @ 80), (6, 22), (N/A), AUTO-BW, IR-CONCURRENT, NO-HT40MINUS, NO-160MHZ, PASSIVE-SCAN
        (5795 - 5815 @ 80), (6, 22), (N/A), AUTO-BW, IR-CONCURRENT, NO-HT40PLUS, NO-160MHZ, PASSIVE-SCAN
        (5815 - 5835 @ 20), (6, 22), (N/A), AUTO-BW, IR-CONCURRENT, NO-HT40MINUS, NO-HT40PLUS, NO-80MHZ, NO-160MHZ, PASSIVE-SCAN
</code></pre></div></div>

<p>That didn’t change anything. The adapter is ignoring our regdom. To be more
precise, it’s applying the global regdom as a mask, which can only further
restrict the adapter’s self-managed regdom. Not that you could further restrict
it much, since it’s already super restrictive. Every single 5GHz channel is
marked DFS. Even worse, they are also marked PASSIVE-SCAN, also known as “no IR”
(initiating radiation). This is a no-go for AP mode, since PASSIVE-SCAN
disallows transmission unless the adapter is associated with an AP.</p>

<p>Now, where does the adapter’s self-managed regdom come from? Turns out it comes
from a feature called Location Aware Regulatory (LAR). LAR is a feature of these
Intel WiFi adapters which allows it to infer its regulatory domain from its
environment. If LAR works
<a href="https://bugzilla.kernel.org/show_bug.cgi?id=206469#c2">reasonably</a>
<a href="https://bugzilla.kernel.org/show_bug.cgi?id=208071">well</a>, then it makes sense
to infer regdom rather than let it be user configurable in order to ensure
compliance with local laws. There used to be a kernel module parameter for
iwlwifi called <code class="language-plaintext highlighter-rouge">lar_disable</code> which allowed disabling LAR. However, it has been
removed since Linux 5.4 as it
<a href="https://bugzilla.kernel.org/show_bug.cgi?id=205695">wasn’t meant to be user-facing</a>.
Today, there is no way to disable LAR.</p>

<p>To summarize, we can’t create an AP on 5GHz because all 5GHz channels are DFS
channels (and also because they are all PASSIVE-SCAN). DFS is broken with this
adapter. They are DFS channels because of the regdom. We can’t change the regdom
because it is inferred with LAR. We can’t disable LAR, because the config
parameter was removed. We can’t have nice things.</p>

<p>At the end of this wild goose chase, I found
<a href="https://www.reddit.com/r/linuxmasterrace/comments/qv0qlj/comment/hkugqqk/?utm_source=reddit&amp;utm_medium=web2x&amp;context=3">this Reddit post</a>.
If it wasn’t for this post, I would be halfway down this list already.</p>]]></content><author><name>netdex</name></author><summary type="html"><![CDATA[I have a thin client that I wanted to turn into a WiFi access point. “Consumer APs are basically just really slow computers. Then, shouldn’t a moderately slow computer be a really fast AP?” In theory, it checked out. Just slap OpenWrt onto it or something of that ilk, and Bob’s your uncle.]]></summary></entry><entry><title type="html">RX 5600 XT BIOS Unlock, 2 GHz and beyond</title><link href="https://netdex.org/2021/02/09/rx-5600-xt-bios-unlock/" rel="alternate" type="text/html" title="RX 5600 XT BIOS Unlock, 2 GHz and beyond" /><published>2021-02-09T00:00:00+00:00</published><updated>2021-02-09T00:00:00+00:00</updated><id>https://netdex.org/2021/02/09/rx-5600-xt-bios-unlock</id><content type="html" xml:base="https://netdex.org/2021/02/09/rx-5600-xt-bios-unlock/"><![CDATA[<p>We are all aware of the previous fiascos
<a href="https://www.techspot.com/news/85113-amd-recommends-radeon-rx-5600-xt-owners-update.html">the RX 5600 XT has been a part of</a>.
As a quick refresher, AMD released a firmware update for the RX 5600 XT to make
it more competitive at its price point. This was in response to a price dip from
NVIDIA on cards competing in the same price range. The update boosted clocks and
power limits, pushing the silicon further than it was originally validated for.
Despite this, AMD still limited the performance of the RX 5600 XT via firmware
and the driver by imposing a hard limit on key parameters<sup id="fnref:1"><a href="#fn:1" class="footnote" rel="footnote" role="doc-noteref">1</a></sup>.
Attempting to push the card further (for example, with soft PowerPlay tables)
causes the card to instantly duck to the lowest clock speed.</p>

<p>To go beyond the limits and break the 2 GHz barrier we need to bypass both the
firmware and driver restrictions. Recently,
<a href="https://www.reddit.com/user/BITBY_RU/">u/BITBY_RU</a> released bonafide unlock
firmare which is capable of bypassing the firmware restrictions placed on this
card. Their intention is for cryptocurrency mining, although they serve their
purpose for boosting gaming performance as well.</p>

<p>The RX 5600 XT actually has a lot of overclocking room, capable of competing
with the RX 5700 and RX 5700 XT. The amount of room ultimately depends on the
silicon quality, which varies from part to part. You may get better or worse
results depending on the silicon lottery.</p>

<p class="message message-warning">
<b>DISCLAIMER:</b> By following the instructions presented in this article, you
are doing so at your own risk. The author is not liable for any consequences
resulting from the procedures and information in this article, which are
provided as-is.
All software and firmware linked to in this article are distributed and hosted
by third-parties. The author does not endorse any third-party linked to in this
article.
Modifying the VBIOS of your graphics card will void your warranty.
Running your hardware outside of manufacturer tolerances for an extended period
of time may lead to instability and/or permanent damage. <u>It is possible that
your graphics card could be bricked.</u> 
</p>

<p>Before continuing, I highly recommend reading through
<a href="https://web.archive.org/web/20231003041802/https://gamersnexus.net/guides/3550-guide-how-to-flash-amd-gpu-vbios-rx-5600-xt">the GamersNexus guide to flashing VBIOS for RX 5600 XT</a>,
which contains useful information regarding recovery in case you brick your
card.</p>

<h2 id="prerequisites">Prerequisites</h2>

<ul>
  <li><a href="https://www.igorslab.de/en/download-area-new-version-of-morepowertool-mpt-and-final-release-of-redbioseditor-rbe/">ATIFLASH v2.93+</a>
<strong>EXACTLY</strong>, do not use newer versions (the “+” is part of the version name,
it doesn’t mean “including versions newer than v2.93”!)</li>
  <li><a href="https://www.igorslab.de/en/red-bios-editor-and-morepowertool-adjust-and-optimize-your-vbios-and-even-more-stable-overclocking-navi-unlimited/">MorePowerTool (MPT) and Red BIOS Editor (RBE)</a></li>
  <li>Unlock firmware for your respective card from
<a href="https://web.archive.org/web/20210414150150/https://bitby.ru/bios-rx5600xt-mod-en.php">bitby.ru</a></li>
</ul>

<p>Please read through the disclaimers in the linked download pages.</p>

<h2 id="creating-the-overdrive-unlock-firmware">Creating the overdrive unlock firmware</h2>

<p>The unlock firmware provided by bitby.ru comes with
<a href="https://www.reddit.com/user/BITBY_RU/comments/l4njxh/unlocked_bios_for_gigabyte_rx_5600_xt_6_gb_gaming/gkzppxz/">default overdrive limits</a>.
Therefore, we will need to create an MPT profile with our desired overdrive
limits and apply it to the unlock firmware.</p>

<h3 id="mpt-profile">MPT profile</h3>

<p>First, open MPT and open the unlock firmware you downloaded earlier. You will
want to modify the overdrive limits and power/voltage to higher values so that
you will be able to overclock it later. For reference, here are the values I
chose:</p>

<p><img src="/assets/images/posts/2021-02-09-rx-5600-xt-bios-unlock/zBoReW5.png" alt="MPT overdrive limits" /></p>

<p>The most notable changes are to the <em>GFX Maximum Clock</em> and <em>Maximum Voltage
GFX</em>. I do not recommend changing <em>Power Limit GPU</em> or <em>TDC Limit GFX</em> unless
you have the thermal headroom.</p>

<p>After you are done configuring, <strong>DO NOT</strong> click <em>Write SPPT</em>. Instead, click
<em>Save</em> and save the MPT profile somewhere.</p>

<h3 id="rbe-modifications">RBE modifications</h3>

<p>Next, open RBE and load the unlock firmware. You will want to change the GPU ID
to <em>5700XT</em>, which will cause the driver to believe the card is an RX 5700 XT
and bypass the driver restrictions. Next, navigate to the <em>PowerPlay</em> tab and
load the MPT profile you created earlier. Click <em>Save</em> and save this modified
unlock firmware somewhere.</p>

<p><img src="/assets/images/posts/2021-02-09-rx-5600-xt-bios-unlock/F7bWIQ5.png" alt="RBE" /></p>

<h2 id="flashing">Flashing</h2>

<p>You will now flash the modified unlock firmware onto your graphics card.
<strong>Ensure you have read the disclaimer above before proceeding.</strong></p>

<p>First, open an elevated command prompt. You can do so by searching “cmd” in the
start menu, right clicking it, and choosing “Run as administrator”.</p>

<p><img src="/assets/images/posts/2021-02-09-rx-5600-xt-bios-unlock/szUwxL5.png" alt="CMD" /></p>

<p>In the elevated command prompt, change directory to where you extracted ATIFLASH
v2.93+ using <code class="language-plaintext highlighter-rouge">cd /d &lt;path&gt;</code>. For example, I extracted it to my desktop, so I
would run <code class="language-plaintext highlighter-rouge">cd /d "C:\Users\netdex\Desktop\293plus"</code>.</p>

<p>Next, copy your modified unlock firmware into this directory. <strong>Make sure there
are not any spaces in the name.</strong> For example, the contents of my directory now
look like this, where <code class="language-plaintext highlighter-rouge">ulfakempt.rom</code> is my modified unlock firmware file.</p>

<div class="language-plaintext highlighter-rouge"><div class="highlight"><pre class="highlight"><code>C:\Users\netdex\Desktop\293plus&gt;dir
 Directory of C:\Users\netdex\Desktop\293plus

02/07/2021  12:36 PM    &lt;DIR&gt;          .
02/07/2021  12:36 PM    &lt;DIR&gt;          ..
02/07/2021  11:12 AM           377,344 amdvbflash.exe
02/07/2021  11:12 AM            12,048 atidgllk.sys
02/07/2021  11:12 AM            22,800 atikia64.sys
02/07/2021  11:12 AM            14,608 atillk64.sys
02/07/2021  11:12 AM             6,446 doc.txt
02/07/2021  11:12 AM               218 how-flash.txt
02/08/2021  09:00 AM           524,288 ulfakempt.rom
              10 File(s)      2,532,546 bytes
               2 Dir(s)  27,770,851,328 bytes free
</code></pre></div></div>

<p>In the elevated command prompt, run <code class="language-plaintext highlighter-rouge">amdvbflash /h</code>. The output should resemble
the following:</p>

<div class="language-plaintext highlighter-rouge"><div class="highlight"><pre class="highlight"><code>C:\Users\netdex\Desktop\293plus&gt;amdvbflash
--- amdvbflash v2.93+ ---
-h, -?, /h, /?          Help (this screen)

Format: amdvbflash [command] [parameter1] [parameter2] [parameter3] &lt;option/s&gt;
--- snip ---
</code></pre></div></div>

<p><strong>Ensure that the version printed is <code class="language-plaintext highlighter-rouge">amdvbflash v2.93+</code> before proceeding.</strong></p>

<p>Now, run <code class="language-plaintext highlighter-rouge">amdvbflash -i</code>. The output should resemble the following:</p>

<div class="language-plaintext highlighter-rouge"><div class="highlight"><pre class="highlight"><code>C:\Users\netdex\Desktop\293plus&gt;amdvbflash -i

adapter bn dn fn dID       asic           flash      romsize test    bios p/n
======= == == == ==== =============== ============== ======= ==== ==============
   0    28 00 00 731F Navi10          W25Q80          100000 pass       -
</code></pre></div></div>

<p>Under the adapter column is the GPU ID for each respective GPU in your system.
Note which GPU ID corresponds to your RX 5600 XT (shown as “Navi10” here).</p>

<p>Before you continue, make a backup of your current firmware in case something
goes wrong. You can do so by running <code class="language-plaintext highlighter-rouge">amdvbflash -s &lt;GPU ID&gt; bios0.rom</code> in the
elevated command prompt, which will save the current firmware into a file called
<code class="language-plaintext highlighter-rouge">bios0.rom</code>.</p>

<p>Now, we will proceed to actually flash the modified unlock firmware onto your
graphics card. <strong>This is the point of no return.</strong></p>

<p>In the elevated command prompt, run
<code class="language-plaintext highlighter-rouge">amdvbflash -f -p &lt;GPU ID&gt; &lt;MODIFIED_UNLOCK_VBIOS.ROM&gt;</code>. For example, I would
run <code class="language-plaintext highlighter-rouge">amdvbflash -f -p 0 ulfakempt.rom</code>. Wait for the flash to successfully
finish, and restart your computer when prompted. If everything went well,
congratulations! Your RX 5600 XT is now fully unlocked, and you can overclock it
to your heart’s desire.</p>

<p>Tuning your overlock is outside of the scope of this article. For reference, the
overclock I was able to achieve is shown below.<sup id="fnref:2"><a href="#fn:2" class="footnote" rel="footnote" role="doc-noteref">2</a></sup> The limiting factor for me
is power consumption/thermals. Again, your results may vary and depend on
silicon quality.</p>

<p><img src="/assets/images/posts/2021-02-09-rx-5600-xt-bios-unlock/zBhiZMR.png" alt="wattman" /></p>

<h2 id="references">References</h2>

<ul>
  <li><a href="https://www.reddit.com/r/overclocking/comments/j9zuyb/tutorial_how_to_unlock_rx_5600_xt/">Tutorial how to unlock rx 5600 xt</a></li>
  <li><a href="https://www.reddit.com/user/BITBY_RU/comments/l4njxh/unlocked_bios_for_gigabyte_rx_5600_xt_6_gb_gaming/">Unlocked bios for Gigabyte RX 5600 XT 6GB GAMING OC 3 Fan GV-R56XTGAMING Samsung + Micron</a></li>
  <li><a href="https://www.reddit.com/user/BITBY_RU/">u/BITBY_RU</a></li>
  <li><a href="https://bitby.ru/bios-rx5600xt-mod-en.php">Unlocked modified BIOS for AMD Radeon RX 5600 XT samsung + micron + hynix</a></li>
</ul>

<h2 id="footnotes">Footnotes</h2>

<div class="footnotes" role="doc-endnotes">
  <ol>
    <li id="fn:1">

      <p>1820 MHz on core clock, 1860 MHz on memory clock, and 180? Watts on ASIC
power <a href="#fnref:1" class="reversefootnote" role="doc-backlink">&#8617;</a></p>
    </li>
    <li id="fn:2">
      <p>core clock 2050 MHz @ 1.1V, mem clock 1800 MHz <a href="#fnref:2" class="reversefootnote" role="doc-backlink">&#8617;</a></p>
    </li>
  </ol>
</div>]]></content><author><name>netdex</name></author><summary type="html"><![CDATA[We are all aware of the previous fiascos the RX 5600 XT has been a part of. As a quick refresher, AMD released a firmware update for the RX 5600 XT to make it more competitive at its price point. This was in response to a price dip from NVIDIA on cards competing in the same price range. The update boosted clocks and power limits, pushing the silicon further than it was originally validated for. Despite this, AMD still limited the performance of the RX 5600 XT via firmware and the driver by imposing a hard limit on key parameters1. Attempting to push the card further (for example, with soft PowerPlay tables) causes the card to instantly duck to the lowest clock speed. To go beyond the limits and break the 2 GHz barrier we need to bypass both the firmware and driver restrictions. Recently, u/BITBY_RU released bonafide unlock firmare which is capable of bypassing the firmware restrictions placed on this card. Their intention is for cryptocurrency mining, although they serve their purpose for boosting gaming performance as well. The RX 5600 XT actually has a lot of overclocking room, capable of competing with the RX 5700 and RX 5700 XT. The amount of room ultimately depends on the silicon quality, which varies from part to part. You may get better or worse results depending on the silicon lottery. DISCLAIMER: By following the instructions presented in this article, you are doing so at your own risk. The author is not liable for any consequences resulting from the procedures and information in this article, which are provided as-is. All software and firmware linked to in this article are distributed and hosted by third-parties. The author does not endorse any third-party linked to in this article. Modifying the VBIOS of your graphics card will void your warranty. Running your hardware outside of manufacturer tolerances for an extended period of time may lead to instability and/or permanent damage. It is possible that your graphics card could be bricked. Before continuing, I highly recommend reading through the GamersNexus guide to flashing VBIOS for RX 5600 XT, which contains useful information regarding recovery in case you brick your card. Prerequisites ATIFLASH v2.93+ EXACTLY, do not use newer versions (the “+” is part of the version name, it doesn’t mean “including versions newer than v2.93”!) MorePowerTool (MPT) and Red BIOS Editor (RBE) Unlock firmware for your respective card from bitby.ru Please read through the disclaimers in the linked download pages. Creating the overdrive unlock firmware The unlock firmware provided by bitby.ru comes with default overdrive limits. Therefore, we will need to create an MPT profile with our desired overdrive limits and apply it to the unlock firmware. MPT profile First, open MPT and open the unlock firmware you downloaded earlier. You will want to modify the overdrive limits and power/voltage to higher values so that you will be able to overclock it later. For reference, here are the values I chose: The most notable changes are to the GFX Maximum Clock and Maximum Voltage GFX. I do not recommend changing Power Limit GPU or TDC Limit GFX unless you have the thermal headroom. After you are done configuring, DO NOT click Write SPPT. Instead, click Save and save the MPT profile somewhere. RBE modifications Next, open RBE and load the unlock firmware. You will want to change the GPU ID to 5700XT, which will cause the driver to believe the card is an RX 5700 XT and bypass the driver restrictions. Next, navigate to the PowerPlay tab and load the MPT profile you created earlier. Click Save and save this modified unlock firmware somewhere. Flashing You will now flash the modified unlock firmware onto your graphics card. Ensure you have read the disclaimer above before proceeding. First, open an elevated command prompt. You can do so by searching “cmd” in the start menu, right clicking it, and choosing “Run as administrator”. In the elevated command prompt, change directory to where you extracted ATIFLASH v2.93+ using cd /d &lt;path&gt;. For example, I extracted it to my desktop, so I would run cd /d "C:\Users\netdex\Desktop\293plus". Next, copy your modified unlock firmware into this directory. Make sure there are not any spaces in the name. For example, the contents of my directory now look like this, where ulfakempt.rom is my modified unlock firmware file. C:\Users\netdex\Desktop\293plus&gt;dir Directory of C:\Users\netdex\Desktop\293plus 02/07/2021 12:36 PM &lt;DIR&gt; . 02/07/2021 12:36 PM &lt;DIR&gt; .. 02/07/2021 11:12 AM 377,344 amdvbflash.exe 02/07/2021 11:12 AM 12,048 atidgllk.sys 02/07/2021 11:12 AM 22,800 atikia64.sys 02/07/2021 11:12 AM 14,608 atillk64.sys 02/07/2021 11:12 AM 6,446 doc.txt 02/07/2021 11:12 AM 218 how-flash.txt 02/08/2021 09:00 AM 524,288 ulfakempt.rom 10 File(s) 2,532,546 bytes 2 Dir(s) 27,770,851,328 bytes free In the elevated command prompt, run amdvbflash /h. The output should resemble the following: C:\Users\netdex\Desktop\293plus&gt;amdvbflash --- amdvbflash v2.93+ --- -h, -?, /h, /? Help (this screen) Format: amdvbflash [command] [parameter1] [parameter2] [parameter3] &lt;option/s&gt; --- snip --- Ensure that the version printed is amdvbflash v2.93+ before proceeding. Now, run amdvbflash -i. The output should resemble the following: C:\Users\netdex\Desktop\293plus&gt;amdvbflash -i adapter bn dn fn dID asic flash romsize test bios p/n ======= == == == ==== =============== ============== ======= ==== ============== 0 28 00 00 731F Navi10 W25Q80 100000 pass - Under the adapter column is the GPU ID for each respective GPU in your system. Note which GPU ID corresponds to your RX 5600 XT (shown as “Navi10” here). Before you continue, make a backup of your current firmware in case something goes wrong. You can do so by running amdvbflash -s &lt;GPU ID&gt; bios0.rom in the elevated command prompt, which will save the current firmware into a file called bios0.rom. Now, we will proceed to actually flash the modified unlock firmware onto your graphics card. This is the point of no return. In the elevated command prompt, run amdvbflash -f -p &lt;GPU ID&gt; &lt;MODIFIED_UNLOCK_VBIOS.ROM&gt;. For example, I would run amdvbflash -f -p 0 ulfakempt.rom. Wait for the flash to successfully finish, and restart your computer when prompted. If everything went well, congratulations! Your RX 5600 XT is now fully unlocked, and you can overclock it to your heart’s desire. Tuning your overlock is outside of the scope of this article. For reference, the overclock I was able to achieve is shown below.2 The limiting factor for me is power consumption/thermals. Again, your results may vary and depend on silicon quality. References Tutorial how to unlock rx 5600 xt Unlocked bios for Gigabyte RX 5600 XT 6GB GAMING OC 3 Fan GV-R56XTGAMING Samsung + Micron u/BITBY_RU Unlocked modified BIOS for AMD Radeon RX 5600 XT samsung + micron + hynix Footnotes 1820 MHz on core clock, 1860 MHz on memory clock, and 180? Watts on ASIC power &#8617; core clock 2050 MHz @ 1.1V, mem clock 1800 MHz &#8617;]]></summary></entry><entry><title type="html">Using Ace.js for Pukiwiki</title><link href="https://netdex.org/2020/10/18/using-ace-js-for-pukiwiki/" rel="alternate" type="text/html" title="Using Ace.js for Pukiwiki" /><published>2020-10-18T00:00:00+00:00</published><updated>2020-10-18T00:00:00+00:00</updated><id>https://netdex.org/2020/10/18/using-ace-js-for-pukiwiki</id><content type="html" xml:base="https://netdex.org/2020/10/18/using-ace-js-for-pukiwiki/"><![CDATA[<p>Out of the box, Pukiwiki’s text editor is a simple textarea. Of course, this
leaves much to be desired, such as:
<!--more--></p>

<ul>
  <li>Auto-indentation</li>
  <li>Syntax highlighting</li>
  <li>Line numbering</li>
  <li>Find/replace</li>
  <li>Vim bindings (a must-have)</li>
  <li>etc.</li>
</ul>

<p>We can use <a href="https://ace.c9.io/">Ace</a> to implement this functionality,
by embedding the text editor in place of the default textarea used for editing.</p>

<h2 id="tutorial">Tutorial</h2>

<p>At the end of <code class="language-plaintext highlighter-rouge">pukiwiki.skin.php</code> before the <code class="language-plaintext highlighter-rouge">&lt;/body&gt;</code> tag, insert
the following two script includes:</p>

<div class="language-html highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="nt">&lt;script </span><span class="na">src=</span><span class="s">"https://ajax.googleapis.com/ajax/libs/jquery/3.4.1/jquery.min.js"</span><span class="nt">&gt;&lt;/script&gt;</span>
<span class="nt">&lt;script </span><span class="na">src=</span><span class="s">"https://cdnjs.cloudflare.com/ajax/libs/ace/1.4.4/ace.js"</span><span class="nt">&gt;&lt;/script&gt;</span>
<span class="nt">&lt;/body&gt;</span>
</code></pre></div></div>

<p>At the end of <code class="language-plaintext highlighter-rouge">main.js</code>, add the following code at the end:</p>

<div class="language-js highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="c1">// adapted from https://stackoverflow.com/a/19513428</span>
<span class="nf">$</span><span class="p">(</span><span class="nf">function </span><span class="p">()</span> <span class="p">{</span>
    <span class="nf">$</span><span class="p">(</span><span class="dl">'</span><span class="s1">textarea[data-editor]</span><span class="dl">'</span><span class="p">).</span><span class="nf">each</span><span class="p">(</span><span class="nf">function </span><span class="p">()</span> <span class="p">{</span>
        <span class="kd">var</span> <span class="nx">textarea</span> <span class="o">=</span> <span class="nf">$</span><span class="p">(</span><span class="k">this</span><span class="p">);</span>
        <span class="kd">var</span> <span class="nx">mode</span> <span class="o">=</span> <span class="nx">textarea</span><span class="p">.</span><span class="nf">data</span><span class="p">(</span><span class="dl">'</span><span class="s1">editor</span><span class="dl">'</span><span class="p">);</span>
        <span class="kd">var</span> <span class="nx">editDiv</span> <span class="o">=</span> <span class="nf">$</span><span class="p">(</span><span class="dl">'</span><span class="s1">&lt;div&gt;</span><span class="dl">'</span><span class="p">,</span> <span class="p">{</span>
            <span class="na">position</span><span class="p">:</span> <span class="dl">'</span><span class="s1">absolute</span><span class="dl">'</span><span class="p">,</span>
            <span class="na">width</span><span class="p">:</span> <span class="nx">textarea</span><span class="p">.</span><span class="nf">width</span><span class="p">(),</span>
            <span class="na">height</span><span class="p">:</span> <span class="nx">textarea</span><span class="p">.</span><span class="nf">height</span><span class="p">(),</span>
            <span class="dl">'</span><span class="s1">class</span><span class="dl">'</span><span class="p">:</span> <span class="nx">textarea</span><span class="p">.</span><span class="nf">attr</span><span class="p">(</span><span class="dl">'</span><span class="s1">class</span><span class="dl">'</span><span class="p">)</span>
        <span class="p">}).</span><span class="nf">insertBefore</span><span class="p">(</span><span class="nx">textarea</span><span class="p">);</span>
        <span class="nx">textarea</span><span class="p">.</span><span class="nf">css</span><span class="p">(</span><span class="dl">'</span><span class="s1">display</span><span class="dl">'</span><span class="p">,</span> <span class="dl">'</span><span class="s1">none</span><span class="dl">'</span><span class="p">);</span>
        <span class="kd">var</span> <span class="nx">editor</span> <span class="o">=</span> <span class="nx">ace</span><span class="p">.</span><span class="nf">edit</span><span class="p">(</span><span class="nx">editDiv</span><span class="p">[</span><span class="mi">0</span><span class="p">]);</span>
        <span class="nx">editor</span><span class="p">.</span><span class="nx">renderer</span><span class="p">.</span><span class="nf">setShowGutter</span><span class="p">(</span><span class="nx">textarea</span><span class="p">.</span><span class="nf">data</span><span class="p">(</span><span class="dl">'</span><span class="s1">gutter</span><span class="dl">'</span><span class="p">));</span>
        <span class="nx">editor</span><span class="p">.</span><span class="nf">setReadOnly</span><span class="p">(</span><span class="nx">textarea</span><span class="p">.</span><span class="nf">data</span><span class="p">(</span><span class="dl">'</span><span class="s1">readonly</span><span class="dl">'</span><span class="p">)</span> <span class="o">===</span> <span class="kc">true</span><span class="p">);</span>
        <span class="nx">editor</span><span class="p">.</span><span class="nf">getSession</span><span class="p">().</span><span class="nf">setValue</span><span class="p">(</span><span class="nx">textarea</span><span class="p">.</span><span class="nf">val</span><span class="p">());</span>
        <span class="nx">editor</span><span class="p">.</span><span class="nf">getSession</span><span class="p">().</span><span class="nf">setMode</span><span class="p">(</span><span class="dl">"</span><span class="s2">ace/mode/</span><span class="dl">"</span> <span class="o">+</span> <span class="nx">mode</span><span class="p">);</span>
        <span class="nx">editor</span><span class="p">.</span><span class="nf">setTheme</span><span class="p">(</span><span class="dl">"</span><span class="s2">ace/theme/chrome</span><span class="dl">"</span><span class="p">);</span>
        <span class="k">if </span><span class="p">(</span><span class="nx">textarea</span><span class="p">.</span><span class="nf">data</span><span class="p">(</span><span class="dl">'</span><span class="s1">grow</span><span class="dl">'</span><span class="p">)</span> <span class="o">===</span> <span class="kc">true</span><span class="p">)</span> <span class="p">{</span>
            <span class="nx">editor</span><span class="p">.</span><span class="nf">setOptions</span><span class="p">({</span>
                <span class="na">maxLines</span><span class="p">:</span> <span class="mi">180</span>
            <span class="p">});</span>
        <span class="p">}</span>

        <span class="c1">// copy back to textarea on form submit...</span>
        <span class="nx">textarea</span><span class="p">.</span><span class="nf">closest</span><span class="p">(</span><span class="dl">'</span><span class="s1">form</span><span class="dl">'</span><span class="p">).</span><span class="nf">submit</span><span class="p">(</span><span class="nf">function </span><span class="p">()</span> <span class="p">{</span>
            <span class="nx">textarea</span><span class="p">.</span><span class="nf">val</span><span class="p">(</span><span class="nx">editor</span><span class="p">.</span><span class="nf">getSession</span><span class="p">().</span><span class="nf">getValue</span><span class="p">());</span>
        <span class="p">})</span>
    <span class="p">});</span>
<span class="p">});</span>
</code></pre></div></div>

<p>In <code class="language-plaintext highlighter-rouge">html.php</code>, change the msg textarea to this:</p>

<div class="language-html highlighter-rouge"><div class="highlight"><pre class="highlight"><code>  <span class="nt">&lt;textarea</span> <span class="na">name=</span><span class="s">"msg"</span> <span class="na">data-editor=</span><span class="s">"c_cpp"</span> <span class="na">data-gutter=</span><span class="s">"true"</span> <span class="na">cols=</span><span class="s">90</span> <span class="na">style=</span><span class="s">"height:80vh"</span><span class="nt">&gt;</span>$s_postdata<span class="nt">&lt;/textarea&gt;</span>
</code></pre></div></div>

<p>Additionally, since we already have Ace loaded, we can use it for syntax highlighting.</p>

<p>The version of Ace.js that this site uses has a special Pukiwiki
syntax highlighter module which implements rudimentary support for
Pukiwiki markup. My fork is available on 
<a href="https://github.com/Netdex/ace/blob/master/lib/ace/mode/pukiwiki_highlight_rules.js">GitHub</a>.
I got a bit lazy trying to translate the context free grammar into regexes, so
the result is a bit incomplete.</p>]]></content><author><name>netdex</name></author><summary type="html"><![CDATA[Out of the box, Pukiwiki’s text editor is a simple textarea. Of course, this leaves much to be desired, such as:]]></summary></entry><entry><title type="html">RX 5600 XT Review</title><link href="https://netdex.org/2020/07/20/rx-5600-xt-review/" rel="alternate" type="text/html" title="RX 5600 XT Review" /><published>2020-07-20T00:00:00+00:00</published><updated>2020-07-20T00:00:00+00:00</updated><id>https://netdex.org/2020/07/20/rx-5600-xt-review</id><content type="html" xml:base="https://netdex.org/2020/07/20/rx-5600-xt-review/"><![CDATA[<p>I recently acquired a Gigabyte RX 5600 XT Gaming OC, hoping that the widely
reported driver issues were an overreaction from a vocal minority.
Unfortunately, this was not the case - I encountered several stability issues
throughout my first few weeks of using this card. This post goes over the
troubleshooting process I went through while trying to get this card to work.
<!--more--></p>

<p>As a foreword, nobody should ever go through this much effort to get a
product they purchased to work properly. If it wasn’t for Canada Computers’
abysmal return policy, I would have jumped ship the instant my screen turned
green.</p>

<h2 id="symptoms">Symptoms</h2>
<p>The most common issue I experienced with this card is the infamous black screen,
characterized by the following symptoms:</p>
<ul>
  <li>The screen suddenly turning black while other computer functionality
remains (such as sound)
    <ul>
      <li>Sometimes, the cursor may still work or flicker</li>
      <li>On screens connected via HDMI, the screen may turn green instead</li>
      <li>After a few seconds, the entire system will stop responding, probably
due to TDR (increasing TdrDelay causes this period of “functionality” to last longer)</li>
    </ul>
  </li>
  <li>Almost always happens while gaming, regardless of whether
the game was graphically intensive or not
    <ul>
      <li>Has occurred randomly during other tasks, such as unlocking my
  workstation or watching a video in MPC-HC</li>
    </ul>
  </li>
</ul>

<h2 id="diagnosis">Diagnosis</h2>

<h3 id="system-memory-timings">System Memory Timings</h3>

<p>When I first got this card, I happened to have slightly unstable RAM timings for my system memory
(about 1 bit flip after 8 passes of memtest). The memory was running at 3000 MT/s 14-16-16-16-32.</p>

<p>I have since then loosened the memory timings to 2933 MT/s 16-17-17-17-35.</p>

<p>Other people in the community have reported <a href="https://community.amd.com/thread/253016">similar issues</a>.
It seems these cards are fairly sensitive to bad memory timings, since I did
not have any issues with these memory timings before with any other cards.</p>

<h3 id="bios">BIOS</h3>
<p>You are probably already aware of the BIOS fiasco AMD sprung on its partners
in order to make the RX 5600 XT more competitive. Gigabyte released two versions,
F2 (stability) and FA0 (performance). I have not been able to achieve system
stability with FA0, despite the only differences between them being VRAM speeds and
power limits. You can replicate the FA0 performance solely with the F2 bios and Wattman.</p>

<p><strong>Update:</strong> As of now, the FA0 VBIOS is no longer available on Gigabyte’s website.</p>

<p>It turns out that there are two different factory VBIOSes from the factory, F1 and F60.
It is worth noting that F1 and F60 share all parameters inspectable
by MorePowerTool. Despite this, Gigabyte notes that the VBIOS upgrade path
to F2 and FA0 is only supported for cards that come with the F1 VBIOS.
That is, cards that come with F60 are inherently less overclockable than
cards that come with F1, <a href="https://community.amd.com/thread/252648">despite being sold as the same product</a>.</p>

<p>After reducing my clock speeds to F60 defaults while having F2 flashed,
I no longer had any black screen issues.</p>

<p><strong>Update:</strong> Gigabyte released three new VBIOS versions: F61, F3 and FA1 - one
corresponding to each previous version. I could not identify any difference in
key parameters between these versions. However, I am able to run F61
without any stability issues (even overclocked, nonetheless).</p>

<h3 id="fan-curves">Fan Curves</h3>
<p>The default fan curves are inadequate, and will almost definitely lead to
thermal throttling. The throttling did not seem to be aggressive enough to
avoid system instability. I ended up ramping up the fan curve by 20% PWM.</p>

<h3 id="chipset-drivers">Chipset Drivers</h3>
<p>I had manufacturer chipset drivers, which were slightly outdated
compared to the chipset drivers directly from AMD.</p>

<h3 id="adrenalin-2020">Adrenalin 2020</h3>
<p>Some people have seen success from installing the display driver <a href="https://www.youtube.com/watch?v=s5rRIZ1wMAY">without the Adrenalin software</a>.
This would imply that there is some component of Adrenalin which causes these black screen
issues. Given the amount of introspection into games that Adrenalin does (counting average FPS,
number of hours played, etc.), it would not be surprising if some of this
functionality is broken.</p>

<p>This would also explain why I have only experienced these black screens
while playing games. All 50 or so of the black screens I have encountered were
while playing a game.</p>

<p>Without Adrenalin, you can still apply overclocks with tools such as
OverdriveNTool. I personally had issues with tools like MSI Afterburner,
as they do not give enough control over voltage curves.</p>

<h2 id="the-verdict">The Verdict</h2>

<p>My leading theory: the card I purchased came with the F60 VBIOS.
Flashing F2 was thus unsupported, which led to the instability. The
instability was caused by either power limits, memory frequency,
or some VBIOS firmware that is not compatible with cards that
come with F60.</p>

<p>I am not hopeful that a future VBIOS with be released as an
upgrade path from F60 (i.e. F61), since it is extremely likely that
cards that come with F60 are just poorer bins which are not capable
of 14 Gbps anyways.</p>

<p>When purchasing this card, there is no way for a consumer to tell
whether they are getting a card with F60 or F1 VBIOS. There are
significant performance differences between these cards. Gigabyte does
not make this clear, with AMD’s official site even listing this card
as upgradable to 14 Gbps.</p>

<p>Do not buy this card.</p>

<p><strong>Update:</strong> Given the new VBIOS updates, this card is now perfectly fine. I
wish Gigabyte gave some sort of statement or postponed release of the card
before the new VBIOS was developed, as it would have saved me many
hours of headaches.</p>]]></content><author><name>netdex</name></author><summary type="html"><![CDATA[I recently acquired a Gigabyte RX 5600 XT Gaming OC, hoping that the widely reported driver issues were an overreaction from a vocal minority. Unfortunately, this was not the case - I encountered several stability issues throughout my first few weeks of using this card. This post goes over the troubleshooting process I went through while trying to get this card to work.]]></summary></entry><entry><title type="html">SMR Drives and UASP</title><link href="https://netdex.org/2020/07/20/smr-drives-and-uasp/" rel="alternate" type="text/html" title="SMR Drives and UASP" /><published>2020-07-20T00:00:00+00:00</published><updated>2020-07-20T00:00:00+00:00</updated><id>https://netdex.org/2020/07/20/smr-drives-and-uasp</id><content type="html" xml:base="https://netdex.org/2020/07/20/smr-drives-and-uasp/"><![CDATA[<p>A part of my workflow involves using <a href="https://github.com/restic">restic</a> to backup data
onto an external hard drive. After a migration, I needed to perform a rather
large ingestion into my restic repo (about 500GB). However, in the middle
of the backup my drive suddenly disappeared without a trace. No drive letter,
not even in device manager.
<!--more--></p>

<p>Thinking it was just a fluke, I unplugged the drive and plugged it back in.
It appeared normally in Explorer, and worked just fine. Ran <code class="language-plaintext highlighter-rouge">chkdsk</code>;
on it and it returned no errors. Weird. Ran the backup again, starting from
the very beginning. The drive disappears again half an hour later.</p>

<p>Something was definitely wrong, so I looked at Event Viewer to see if
Windows agreed with me. I saw a number of worrying warnings…</p>

<p><img src="https://i.imgur.com/A2mwDlx.png" alt="Event Viewer" /></p>

<p>Namely, <em>UASPStor: Reset to device, \Device\RaidPort3, was issued.</em>
Burn these words into your mind. Precursory search results pointed
towards hardware failure or system instability. I didn’t buy it.</p>

<p>I shucked the drive out of the external enclosure, connected it directly
via a SATA interface, and ran the backup again. After about half an hour,
the backup is still running. But the speed dropped to a whopping 1 MB/s.
And the average access latency was into the minutes.</p>

<p>I stopped the backup again, frustrated. In complete silence, I tried to
come up with any fathomable reason why I could not backup my files. That was
when I heard a peculiar noise. The clickety-clackety sound my hard drive makes
when it moves to do writes.</p>

<p>I open task manager, and check the current write speed to the drive. Zero.
It was at this moment that everything became abundantly clear.</p>

<p>My external drive probably uses <a href="https://en.wikipedia.org/wiki/Shingled_magnetic_recording">Shingled Magnetic Recording</a> 
(DM-SMR specifically).
One fantastic aspect of SMR drives is that they have a much better data density
than conventional PMR/CMR drives. One less fantastic aspect of SMR drives is
that their write speeds are complete garbage.</p>

<p>SMR drives abuse the fact that you can read from tracks much narrower than
you can write, by stacking them on top of each other like the shingles on a roof.
This makes them great for write-once, read-many applications, since read performance
is not affected. However, if you want to write even a single sector to the drive,
you will have to read and write the entire zone of tracks that overlap.
Your 4K write just turned into a 256MB one.</p>

<p>Device-managed SMR drives (DM-SMR) make the fact that they are SMR drives
completely transparent to the host system. That is, they expose a normal
interface and handle all the additional work that SMR drives need to do. Since
SMR drives have amazingly awful write performance, many levels of caching
are usually added to improve performance. For example, a DM-SMR drive may have
a fairly large PMR cache, and a relatively small DRAM cache.
The drive will try to optimize the use of the PMR region and the DRAM cache
to avoid having SMR writes in the critical path. When are idling, the
drive controller will slowly siphon data from the PMR cache to the SMR
drive, freeing up PMR cache space.</p>

<p>What happens when the PMR cache is full? Writes go directly to the SMR drive.
You know what SMR drives are really bad at? Writing. You know what
SMR drives are even worse at doing? Random writing. You know what restic
likes to do alot? Yeah. The drive shits the bed so hard that the UASP controller
thinks the drive is literally gone.</p>

<p>Who’s fault is this?</p>

<ol>
  <li>The UASPStor driver probably needs to handle timeouts better than it does right now, since apparently
minutes of access latency are a real use case.</li>
  <li>Drive manufacturers need to label their disks with the technology they use.</li>
  <li>Software writers need to optimize for what spinning disks do great: sequential reads and writes.</li>
</ol>

<p>Let block sizes be configurable, because sometimes I want to waste a bit of space if it means saving hours of time.</p>

<p><code class="language-plaintext highlighter-rouge">&lt;/rant&gt;</code></p>]]></content><author><name>netdex</name></author><summary type="html"><![CDATA[A part of my workflow involves using restic to backup data onto an external hard drive. After a migration, I needed to perform a rather large ingestion into my restic repo (about 500GB). However, in the middle of the backup my drive suddenly disappeared without a trace. No drive letter, not even in device manager.]]></summary></entry><entry><title type="html">Building LineageOS 17.1 on ArchLinux</title><link href="https://netdex.org/2020/04/13/building-lineageos-17-1-on-archlinux/" rel="alternate" type="text/html" title="Building LineageOS 17.1 on ArchLinux" /><published>2020-04-13T00:00:00+00:00</published><updated>2020-04-13T00:00:00+00:00</updated><id>https://netdex.org/2020/04/13/building-lineageos-17-1-on-archlinux</id><content type="html" xml:base="https://netdex.org/2020/04/13/building-lineageos-17-1-on-archlinux/"><![CDATA[<p>This post serves as a log of some of the hoops I had to jump through in
order to get LineageOS 17.1 to build on ArchLinux, as a reminder to myself
in case I need to do this again in the future. As a result, it is rather brief
and incomplete. Consider filling in the gaps with the official build
documentation from the <a href="https://wiki.lineageos.org/devices/mata/build">LineageOS Wiki</a>.
<!--more--></p>

<p>This guide assumes working knowledge of ArchLinux and Android development,
but makes an effort to link to relevant reading.</p>

<h1 id="prerequisites">Prerequisites</h1>
<h2 id="packages">Packages</h2>
<h3 id="required-for-build">Required for build</h3>
<ul>
  <li>repo</li>
  <li><a href="https://aur.archlinux.org/packages/lineageos-devel/">lineageos-devel</a></li>
  <li><a href="https://wiki.archlinux.org/index.php/Official_repositories#multilib">multilib-devel</a></li>
  <li>base-devel</li>
  <li>ttf-dejavu (Fun fact: It’s used to generate the PNG image for the LOS recovery splash screen)</li>
  <li>lib32-ncurses5-compat-libs</li>
  <li>…</li>
</ul>

<h3 id="recommended">Recommended</h3>
<ul>
  <li>android-tools (platform-tools)</li>
  <li>android-udev (udev rules for Android ADB)</li>
</ul>

<h2 id="system-considerations">System considerations</h2>
<p>In my experience, the following system specifications are a bare minimum
for an enjoyable development experience:</p>
<ul>
  <li>A 64-bit multiscalar processor from this decade</li>
  <li>200GB of solid state storage</li>
  <li>'’At least’’ 16 GB of RAM, at the bare minimum 8 GB with some tweaks</li>
  <li>A relatively cool climate and a nearby window to vent heat</li>
</ul>

<h3 id="making-it-work">Making it work</h3>
<p>There are a number of optimizations which can be performed to ‘'’make it work’’’
despite not having the best computer or internet connection:</p>

<h4 id="reduce-simultaneous-jobs">Reduce simultaneous jobs</h4>
<p>For <code class="language-plaintext highlighter-rouge">repo sync</code> or <code class="language-plaintext highlighter-rouge">brunch</code> consider using fewer simultaneous
jobs (via the <code class="language-plaintext highlighter-rouge">-j</code> flag). This has the effect of:</p>
<ul>
  <li>Using less memory at once</li>
  <li>Using less network bandwidth at once (for repo)</li>
  <li>Using less compute resources at once</li>
  <li>Making your build take proportionally longer</li>
</ul>

<h4 id="build-components-individually">Build components individually</h4>
<p>For systems with less than 16 GB of RAM, there are some parts of the build
which will OOM (I’m looking at you, metalava). These components can be
selectively built individually, to reduce the number of tasks your computer
is processing at once (see the top of &amp;pre(build/envsetup.sh); for make commands).</p>

<h4 id="use-swap-and-zswap">Use swap and zswap</h4>
<p>You should probably use
<a href="https://wiki.archlinux.org/index.php/Swap">swap</a> on relatively fast storage
in order to handle sudden spikes in memory usage.</p>

<p>This includes enabling
<a href="https://wiki.archlinux.org/index.php/Zswap">zswap</a>. Many build steps allocate
large heaps or spawn  many JVMs which fill all your resident memory. Memory
compression is surprisingly effective at handling these bad actors.
Without sufficient RAM and swap/zswap disabled, you will probably OOM while generating
documentation with metalava.</p>

<h4 id="caching">Caching</h4>
<p>For multiple builds, consider enabling ccache with or without compression
to reduce build time for subsequent builds.</p>

<div class="language-bash highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="nb">export </span><span class="nv">USE_CCACHE</span><span class="o">=</span>1
<span class="nb">export </span><span class="nv">CCACHE_COMPRESS</span><span class="o">=</span>1
ccache <span class="nt">-M</span> 20G
</code></pre></div></div>

<h1 id="build">Build</h1>

<h2 id="acquiring-sources-and-binaries">Acquiring sources and binaries</h2>
<p>Run the following repo commands to download the sources from a hodgepodge of
repositories scattered across the internet.</p>

<div class="language-bash highlighter-rouge"><div class="highlight"><pre class="highlight"><code>repo init <span class="nt">-u</span> https://github.com/LineageOS/android.git <span class="nt">-b</span> lineage-17.1
repo <span class="nb">sync</span>
</code></pre></div></div>

<p>Consider using fewer simultaneous jobs if you
have a poor internet connection.</p>

<div class="language-bash highlighter-rouge"><div class="highlight"><pre class="highlight"><code>repo <span class="nb">sync</span> <span class="nt">-j1</span> <span class="nt">-c</span>
</code></pre></div></div>

<p>Make sure you keep the <code class="language-plaintext highlighter-rouge">-c</code> flag, which tells repo to only download the
current branch. Otherwise, you will have a bad time.</p>

<h3 id="proprietary-blobs">Proprietary blobs</h3>
<p>You will need proprietary blobs from your device vendor. These can be
extracted from a working device, ripped from an image, or obtained via unofficial means.</p>

<p>If you end up getting blobs from a repository, consider adding them to
your local manifests. This will make synchronization of your blobs work with repo.</p>

<p>For example, my <code class="language-plaintext highlighter-rouge">.repo/local_manifests/roomservice.xml</code> looks like</p>

<div class="language-xml highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="cp">&lt;?xml version="1.0" encoding="UTF-8"?&gt;</span>
<span class="nt">&lt;manifest&gt;</span>
  <span class="nt">&lt;project</span> <span class="na">path=</span><span class="s">"device/essential/mata"</span> <span class="na">remote=</span><span class="s">"github"</span> <span class="na">name=</span><span class="s">"LineageOS/android_device_essential_mata"</span> <span class="nt">/&gt;</span>
  <span class="nt">&lt;project</span> <span class="na">path=</span><span class="s">"kernel/essential/msm8998"</span> <span class="na">remote=</span><span class="s">"github"</span> <span class="na">name=</span><span class="s">"LineageOS/android_kernel_essential_msm8998"</span> <span class="nt">/&gt;</span>
  <span class="nt">&lt;project</span> <span class="na">path=</span><span class="s">"vendor/essential"</span> <span class="na">remote=</span><span class="s">"github"</span> <span class="na">name=</span><span class="s">"TheMuppets/proprietary_vendor_essential"</span> <span class="nt">/&gt;</span>
<span class="nt">&lt;/manifest&gt;</span>
</code></pre></div></div>

<h3 id="device-specific-sources">Device specific sources</h3>
<p>To get vendor and device trees for your specific device, you
will need to have breakfast. This will fail if your proprietary blobs are
missing.</p>

<div class="language-bash highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="nb">source </span>build/envsetup.sh
breakfast &lt;DEVICE_CODENAME&gt;
</code></pre></div></div>

<h3 id="building">Building</h3>
<p>Run the following commands to create an unsigned build:</p>

<div class="language-bash highlighter-rouge"><div class="highlight"><pre class="highlight"><code>croot
brunch &lt;DEVICE_CODENAME&gt;
</code></pre></div></div>

<p>Signed builds can be created via a different process, which I will
not <a href="https://wiki.lineageos.org/signing_builds.html">get into detail</a>.</p>

<p>Your build artifacts will be in the &amp;pre($OUT); directory.</p>

<h3 id="troubleshooting">Troubleshooting</h3>
<p>Something will probably break while you are trying to build. Here’s
what broke for me, and how I tried to fix it.</p>

<h4 id="various-symptoms-related-to-out-of-memory-conditions">Various symptoms related to out-of-memory conditions</h4>
<ul>
  <li>Process is randomly terminated while building</li>
  <li><code class="language-plaintext highlighter-rouge">[ 97% 99068/101261] //frameworks/base:system-api-stubs-docs Metalava [common]</code></li>
</ul>

<p>It was probably killed by the OOM killer. Watch your available memory
and process oom scores while building. Consider using the memory
techniques mentioned above. If all else fails, try tweaking various
environment variables to try to lower memory usage (e.g. Java heap size,
cache sizes etc.)</p>

<div class="language-plaintext highlighter-rouge"><div class="highlight"><pre class="highlight"><code>ERROR: Failed to run command '['simg2img', '/tmp/targetfiles-oa64tct3/IMAGES/system.img', '/tmp/targetfiles-oa64tct3/IMAGES/unsparse_system.img']' (exit code 255):
Failed to read sparse file
</code></pre></div></div>

<ul>
  <li>platform_build/build_image uses /tmp to unsparse image files. On most systems,
/tmp is backed by RAM, with a maximum size of half the available RAM. The random
file used by the tool is generated with &amp;pre(mktemp);, so use the &amp;pre(TMPDIR);
envvar to set it to somewhere else.</li>
</ul>

<h4 id="error-vendorlineagebuildsoongandroidbp308-module-generated_kernel_includes-cmd-unknown-variable-path_override_soong"><code class="language-plaintext highlighter-rouge">error: vendor/lineage/build/soong/Android.bp:30:8: module "generated_kernel_includes": cmd: unknown variable '$(PATH_OVERRIDE_SOONG)'</code></h4>
<p>Have you ever found an <a href="https://mlog.club/article/3069189">article in a foreign language</a> (navigate at your own risk) that seems to describe the very issue you are having, with
no response. Well I have.</p>]]></content><author><name>netdex</name></author><summary type="html"><![CDATA[Not enough ram edition (tm)]]></summary></entry><entry><title type="html">Atelier Ryza: Ever Stuttering and the Stupid Workaround</title><link href="https://netdex.org/2020/02/08/atelier-ryza-ever-stuttering-and-the-stupid-workaround/" rel="alternate" type="text/html" title="Atelier Ryza: Ever Stuttering and the Stupid Workaround" /><published>2020-02-08T00:00:00+00:00</published><updated>2020-02-08T00:00:00+00:00</updated><id>https://netdex.org/2020/02/08/atelier-ryza-ever-stuttering-and-the-stupid-workaround</id><content type="html" xml:base="https://netdex.org/2020/02/08/atelier-ryza-ever-stuttering-and-the-stupid-workaround/"><![CDATA[<p>Like every other Atelier game, the PC port leaves much to be desired,
especially in terms of input. A lack of mouse support, along with
only direct mappings from keys to controller inputs (which are
non-intuitive at best) takes a bit of time to get used to.
<!--more--></p>

<p>As of version 1.03, the Steam version of Atelier Ryza has some stuttering issues.
About every 1-2 seconds, you will probably experience some frame skips.
Accompanied by these frame skips are the physics engine glitching out, which
probably means the physics are tied to the rendering loop. Fantastic.</p>

<p>You might be wondering: how did something so obviously broken make it past
QA? Well, wonder no more.</p>

<p>Every 1-2 seconds, the game polls the XINPUT library for controller input.
This poll either has a timeout, or has a long handler in the event of failure.
The input polling is probably done in the rendering loop, which causes
the frame stutter. The people at Gust probably did their QA with a
controller plugged in (they would probably get an RSI from testing with a
keyboard all day), and the glaringly obvious problem slipped into release.</p>

<p>How can you work around this? The easiest way would be to get a controller.
If you don’t have one, then you can use <a href="http://vjoystick.sourceforge.net/site/">vJoy</a>
to emulate a joystick.</p>

<p><img src="https://i.imgur.com/y8Kw8aQ.png" alt="Device Manager" /></p>

<p>And no, I don’t mean “use vJoy as a controller to play
the game” (which would require something like x360ce to send inputs to the
driver anyways). I mean “install vJoy ‘‘literally’’ just so the game thinks
you have a controller plugged in”, so the XINPUT polling doesn’t fail. You’ll
still be playing with the keyboard.</p>

<p>It’s <code class="language-plaintext highlighter-rouge">$(current year)</code> folks.</p>]]></content><author><name>netdex</name></author><summary type="html"><![CDATA[Like every other Atelier game, the PC port leaves much to be desired, especially in terms of input. A lack of mouse support, along with only direct mappings from keys to controller inputs (which are non-intuitive at best) takes a bit of time to get used to.]]></summary></entry><entry><title type="html">Supporting Legacy of Lunatic Kingdom w/ Twinject</title><link href="https://netdex.org/2018/03/04/extending-twinject-lolk/" rel="alternate" type="text/html" title="Supporting Legacy of Lunatic Kingdom w/ Twinject" /><published>2018-03-04T00:00:00+00:00</published><updated>2018-03-04T00:00:00+00:00</updated><id>https://netdex.org/2018/03/04/extending-twinject-lolk</id><content type="html" xml:base="https://netdex.org/2018/03/04/extending-twinject-lolk/"><![CDATA[<p><em>twinject</em> (<strong>t</strong>ouhou <strong>w</strong>indows <strong>inject</strong>or) is an automated player for the bullet hell games from the <em>Touhou Project</em>. Instead of using computer vision techniques to extract data from the game, a DLL is injected and the relevant game data is directly extracted through trampolined functions.
<!--more--></p>

<p>For the unaware, bullet hells are a category of <em>Shoot ‘em Up</em> video games where the player controls a ship, which must dodge large numbers of obstacles and destroy large numbers of enemies. In the demonstration video, the fast moving projectiles are the obstacles which must be dodged - if the player hits any of these projectiles they die immediately. The player itself also fires projectiles, which damage enemies. The player is the little red sprite near the bottom of the screen. Props to ZUN for making games that people still play 20 years later.</p>

<p>If you want to see a prototype, you can check out the video demonstration below.</p>
<h3 id="video-demonstration-legacy-of-lunatic-kingdom"><a href="https://youtu.be/9WElfhVE-Lk">Video Demonstration (Legacy of Lunatic Kingdom)</a></h3>

<p>Theoretically, Twinject supports every Windows game in the Touhou Project. In order to support a game, four main components must be implemented, which I will briefly detail below.</p>

<h2 id="components">Components</h2>
<h3 id="graphical-control">Graphical Control</h3>
<p>Twinject requires control over the game graphics in order to draw debugging information which is useful in the development of the other components.</p>

<p>All mainline Touhou games can use Direct3D9 for their graphics (older games through the dxd8-&gt;dxd9 patch). Thus, this component is not game specific and only needs to be implemented once.</p>

<p>This component is implemented as a object wrapper delivered to the game via trampolining Direct3DCreate9.</p>

<h3 id="input-control">Input Control</h3>
<p>Twnject requires control over the game input in order to control the player.</p>

<p>All mainline Touhou games can use DirectInput8 for their input (configurable with config.exe). Thus, this component is not game specific and only needs to be implemented once.</p>

<p>This component is implemented as a object wrapper delivered to the game via trampolining DirectInput8Create.</p>

<h3 id="specific-data-processor">Specific Data Processor</h3>
<p>Twinject requires game data in order to be aware of the player’s environment. This includes,</p>

<ul>
  <li>Player location, velocity</li>
  <li>Bullet locations, velocities</li>
  <li>Powerup locations, velocities</li>
  <li>Enemy locations, velocities</li>
  <li>Laser functions</li>
</ul>

<p>The game engines for each Touhou game vary wildly - this component is game specific and must be implemented for each game.</p>

<p>This component is usually implemented as a function hook.</p>

<h3 id="algorithm">Algorithm</h3>
<p>Twinject requires an algorithm to turn game data into player inputs. The following algorithms are currently supported:</p>

<ul>
  <li>Method of Virtual Potential Fields</li>
  <li>Method of Velocity Obstacles</li>
  <li>Method of Vector-based Velocity Projection</li>
</ul>

<p>I will explain these algorithms in detail in their own post.</p>

<h2 id="implementation">Implementation</h2>
<p>Twinject provides a layer of abstraction between components, allowing new components to be easily created and connected together to create a functional automated player for a certain game.</p>

<p>Implementation of game-specific components is usually quite involved, and requires reverse-engineering the game. In this article, I will detail the required procedure to implement a game-specific component for Legacy of Lunatic Kingdom (LoLK).</p>

<h1 id="lolk-specific-data-processor">LoLK Specific Data Processor</h1>
<p>I will detail the specific component required for processing data in LoLK.</p>

<h2 id="collidable-locations">Collidable Locations</h2>
<p>The player requires the locations and size of every collidable entity, so that it may dodge them. This includes the player, bullets, lasers, etc. Collidable entities may be demarcated into two types: function-based, and point-based.</p>

<p>The player and bullets are point-based, and are defined as a point with a radius (LoLK uses hit-circles).</p>

<p>Lasers are function-based, and are defined as a curve with a restricted domain.</p>

<p>For now, we will only talk about point-based collidable entities (PBCE).</p>

<h3 id="obtaining-data">Obtaining Data</h3>
<p>We want to trampoline a function which processes PBCEs once per frame. It would make sense for the game to have a function that checks for collisions between two PBCE.</p>

<p><a href="http://cheater.seesaa.net/article/169529488.html">This website</a> details many memory patches that can be used to modify the functionality of Touhou games. An interesting one is</p>
<div class="language-plaintext highlighter-rouge"><div class="highlight"><pre class="highlight"><code>;無敵
00456540-C3     // set byte at 00456540 to C3
</code></pre></div></div>
<p>which renders the player invincible.</p>

<p>Taking a look in a disassembler (IDA), we can see that setting that byte causes a function to return immediately when called.</p>

<p><img src="https://i.imgur.com/HwGv6Oi.png" alt="ida death" /></p>

<p><em>A disassembly in IDA, showing the function that the patch applies to, and its effect. (note that any variable names and comments were added in myself to help me reverse-engineer the game, and were not present beforehand)</em></p>

<p>Setting a breakpoint in Ollydbg, we can see that this function is called after the player hits an obstacle. This function then carries out the death routine (reduce lives, set invincibility for a while, put player at origin etc.)</p>

<p>Checking cross references to this function, we find the following function:</p>

<p><img src="https://i.imgur.com/6fKBAT9.png" alt="ida col" /></p>

<p><em>A rough decompilation of the function, with variable names annotated based on intuition</em></p>

<p>We can see that this function checks for player collision with PBCEs using the square Euclidean distance check for circle collision. 
If the PBCEs collide, then it calls the death function we found earlier.</p>

<p>We can write a wrapper function that will extract the relevant data for Twinject to use:</p>

<p><img src="https://i.imgur.com/7ubJU4E.png" alt="wrapper" /></p>

<p><em>Hook function for collision check function</em></p>

<p>This wrapper function will be part of the component. Unfortunately the function is usercall, so we must write our own prolog and epilog code with inline assembly.</p>]]></content><author><name>netdex</name></author><summary type="html"><![CDATA[Extending Twinject's capabilities by supporting an additional game]]></summary></entry></feed>