{"id":99465,"date":"2018-08-10T07:00:00","date_gmt":"2018-08-10T21:00:00","guid":{"rendered":"https:\/\/blogs.msdn.microsoft.com\/oldnewthing\/?p=99465"},"modified":"2019-03-13T00:38:14","modified_gmt":"2019-03-13T07:38:14","slug":"20180810-00","status":"publish","type":"post","link":"https:\/\/devblogs.microsoft.com\/oldnewthing\/20180810-00\/?p=99465","title":{"rendered":"The PowerPC 600 series, part 5: Rotates and shifts"},"content":{"rendered":"<p>The Swiss army knife instruction of the PowerPC 600 series instruction set is <code>rlwinm<\/code>, which stands for &#8220;rotate left word immediate and mask.&#8221; <\/p>\n<pre>\n    rlwinm  rd, ra, imm5a, imm5b, imm5c\n    rlwinm. rd, ra, imm5a, imm5b, imm5c ; also updates cr0\n<\/pre>\n<p>This instruction does everything except wash your floor. <\/p>\n<p>First it takes the current value of the <var>ra<\/var> register and rotates it left by <var>imm5a<\/var> bits. Then it keeps all the bits <var>imm5b<\/var> to <var>imm5c<\/var>, inclusive, and clears all the other bits. If <var>imm5b<\/var> is greater than <var>imm5c<\/var>, the bits to keep wrap around! (Don&#8217;t forget that bit zero is the most significant bit.) The result of all this is placed in the <var>rd<\/var> register. <\/p>\n<p>Let&#8217;s take a few examples. <\/p>\n<pre>\n    rlwinm  rd, ra, 5, 6, 20\n<\/pre>\n<p>Here, we take <var>ra<\/var>, rotate it left by 5 bits, and then keep bits 6 through 20. Since bit 0 is the most significant bit, then that means that the mask is <\/p>\n<table BORDER=\"0\" CELLPADDING=\"3\" CLASS=\"cp3\" STYLE=\"border-collapse: collapse;text-align: center\">\n<tr>\n<td STYLE=\"width: 1pc\">0<\/td>\n<td STYLE=\"width: 1pc\">1<\/td>\n<td STYLE=\"width: 1pc\">2<\/td>\n<td STYLE=\"width: 1pc\">3<\/td>\n<td STYLE=\"width: 1pc\">4<\/td>\n<td STYLE=\"width: 1pc\">5<\/td>\n<td STYLE=\"width: 1pc\">6<\/td>\n<td STYLE=\"width: 1pc\">7<\/td>\n<td STYLE=\"width: 1pc\">8<\/td>\n<td STYLE=\"width: 1pc\">9<\/td>\n<td STYLE=\"width: 1pc\">10<\/td>\n<td STYLE=\"width: 1pc\">11<\/td>\n<td STYLE=\"width: 1pc\">12<\/td>\n<td STYLE=\"width: 1pc\">13<\/td>\n<td STYLE=\"width: 1pc\">14<\/td>\n<td STYLE=\"width: 1pc\">15<\/td>\n<td STYLE=\"width: 1pc\">16<\/td>\n<td STYLE=\"width: 1pc\">17<\/td>\n<td STYLE=\"width: 1pc\">18<\/td>\n<td STYLE=\"width: 1pc\">19<\/td>\n<td STYLE=\"width: 1pc\">20<\/td>\n<td STYLE=\"width: 1pc\">21<\/td>\n<td STYLE=\"width: 1pc\">22<\/td>\n<td STYLE=\"width: 1pc\">23<\/td>\n<td STYLE=\"width: 1pc\">24<\/td>\n<td STYLE=\"width: 1pc\">25<\/td>\n<td STYLE=\"width: 1pc\">26<\/td>\n<td STYLE=\"width: 1pc\">27<\/td>\n<td STYLE=\"width: 1pc\">28<\/td>\n<td STYLE=\"width: 1pc\">29<\/td>\n<td STYLE=\"width: 1pc\">30<\/td>\n<td STYLE=\"width: 1pc\">31<\/td>\n<\/tr>\n<tr>\n<td STYLE=\"border: solid 1px black\">0<\/td>\n<td STYLE=\"border: solid 1px black\">0<\/td>\n<td STYLE=\"border: solid 1px black\">0<\/td>\n<td STYLE=\"border: solid 1px black\">0<\/td>\n<td STYLE=\"border: solid 1px black\">0<\/td>\n<td STYLE=\"border: solid 1px black\">0<\/td>\n<td STYLE=\"border: solid 1px black\">1<\/td>\n<td STYLE=\"border: solid 1px black\">1<\/td>\n<td STYLE=\"border: solid 1px black\">1<\/td>\n<td STYLE=\"border: solid 1px black\">1<\/td>\n<td STYLE=\"border: solid 1px black\">1<\/td>\n<td STYLE=\"border: solid 1px black\">1<\/td>\n<td STYLE=\"border: solid 1px black\">1<\/td>\n<td STYLE=\"border: solid 1px black\">1<\/td>\n<td STYLE=\"border: solid 1px black\">1<\/td>\n<td STYLE=\"border: solid 1px black\">1<\/td>\n<td STYLE=\"border: solid 1px black\">1<\/td>\n<td STYLE=\"border: solid 1px black\">1<\/td>\n<td STYLE=\"border: solid 1px black\">1<\/td>\n<td STYLE=\"border: solid 1px black\">1<\/td>\n<td STYLE=\"border: solid 1px black\">1<\/td>\n<td STYLE=\"border: solid 1px black\">0<\/td>\n<td STYLE=\"border: solid 1px black\">0<\/td>\n<td STYLE=\"border: solid 1px black\">0<\/td>\n<td STYLE=\"border: solid 1px black\">0<\/td>\n<td STYLE=\"border: solid 1px black\">0<\/td>\n<td STYLE=\"border: solid 1px black\">0<\/td>\n<td STYLE=\"border: solid 1px black\">0<\/td>\n<td STYLE=\"border: solid 1px black\">0<\/td>\n<td STYLE=\"border: solid 1px black\">0<\/td>\n<td STYLE=\"border: solid 1px black\">0<\/td>\n<td STYLE=\"border: solid 1px black\">0<\/td>\n<\/tr>\n<tr>\n<td COLSPAN=\"4\" STYLE=\"border: 1px black;border-style: none solid\"><code>0<\/code><\/td>\n<td COLSPAN=\"4\" STYLE=\"border: 1px black;border-style: none solid\"><code>3<\/code><\/td>\n<td COLSPAN=\"4\" STYLE=\"border: 1px black;border-style: none solid\"><code>F<\/code><\/td>\n<td COLSPAN=\"4\" STYLE=\"border: 1px black;border-style: none solid\"><code>F<\/code><\/td>\n<td COLSPAN=\"4\" STYLE=\"border: 1px black;border-style: none solid\"><code>F<\/code><\/td>\n<td COLSPAN=\"4\" STYLE=\"border: 1px black;border-style: none solid\"><code>8<\/code><\/td>\n<td COLSPAN=\"4\" STYLE=\"border: 1px black;border-style: none solid\"><code>0<\/code><\/td>\n<td COLSPAN=\"4\" STYLE=\"border: 1px black;border-style: none solid\"><code>0<\/code><\/td>\n<\/tr>\n<\/table>\n<p>This comes out to <code>0x03FFF800<\/code>. Therefore, the result of the operation is <\/p>\n<pre>\n     rd = rotl(ra, 5) &amp; 0x03FFF800\n<\/pre>\n<p>The other case is where the bit count wraps around: <\/p>\n<pre>\n    rlwinm  rd, ra, 5, 20, 6\n<\/pre>\n<p>This time, we start by setting bit 20, and continue to the end of the word, and then wrap around and start setting bits starting at bit 0, and then finally stop when we get to bit 6. <\/p>\n<table BORDER=\"0\" CELLPADDING=\"3\" CLASS=\"cp3\" STYLE=\"border-collapse: collapse;text-align: center\">\n<tr>\n<td STYLE=\"width: 1pc\">0<\/td>\n<td STYLE=\"width: 1pc\">1<\/td>\n<td STYLE=\"width: 1pc\">2<\/td>\n<td STYLE=\"width: 1pc\">3<\/td>\n<td STYLE=\"width: 1pc\">4<\/td>\n<td STYLE=\"width: 1pc\">5<\/td>\n<td STYLE=\"width: 1pc\">6<\/td>\n<td STYLE=\"width: 1pc\">7<\/td>\n<td STYLE=\"width: 1pc\">8<\/td>\n<td STYLE=\"width: 1pc\">9<\/td>\n<td STYLE=\"width: 1pc\">10<\/td>\n<td STYLE=\"width: 1pc\">11<\/td>\n<td STYLE=\"width: 1pc\">12<\/td>\n<td STYLE=\"width: 1pc\">13<\/td>\n<td STYLE=\"width: 1pc\">14<\/td>\n<td STYLE=\"width: 1pc\">15<\/td>\n<td STYLE=\"width: 1pc\">16<\/td>\n<td STYLE=\"width: 1pc\">17<\/td>\n<td STYLE=\"width: 1pc\">18<\/td>\n<td STYLE=\"width: 1pc\">19<\/td>\n<td STYLE=\"width: 1pc\">20<\/td>\n<td STYLE=\"width: 1pc\">21<\/td>\n<td STYLE=\"width: 1pc\">22<\/td>\n<td STYLE=\"width: 1pc\">23<\/td>\n<td STYLE=\"width: 1pc\">24<\/td>\n<td STYLE=\"width: 1pc\">25<\/td>\n<td STYLE=\"width: 1pc\">26<\/td>\n<td STYLE=\"width: 1pc\">27<\/td>\n<td STYLE=\"width: 1pc\">28<\/td>\n<td STYLE=\"width: 1pc\">29<\/td>\n<td STYLE=\"width: 1pc\">30<\/td>\n<td STYLE=\"width: 1pc\">31<\/td>\n<\/tr>\n<tr>\n<td STYLE=\"border: solid 1px black\">1<\/td>\n<td STYLE=\"border: solid 1px black\">1<\/td>\n<td STYLE=\"border: solid 1px black\">1<\/td>\n<td STYLE=\"border: solid 1px black\">1<\/td>\n<td STYLE=\"border: solid 1px black\">1<\/td>\n<td STYLE=\"border: solid 1px black\">1<\/td>\n<td STYLE=\"border: solid 1px black\">1<\/td>\n<td STYLE=\"border: solid 1px black\">0<\/td>\n<td STYLE=\"border: solid 1px black\">0<\/td>\n<td STYLE=\"border: solid 1px black\">0<\/td>\n<td STYLE=\"border: solid 1px black\">0<\/td>\n<td STYLE=\"border: solid 1px black\">0<\/td>\n<td STYLE=\"border: solid 1px black\">0<\/td>\n<td STYLE=\"border: solid 1px black\">0<\/td>\n<td STYLE=\"border: solid 1px black\">0<\/td>\n<td STYLE=\"border: solid 1px black\">0<\/td>\n<td STYLE=\"border: solid 1px black\">0<\/td>\n<td STYLE=\"border: solid 1px black\">0<\/td>\n<td STYLE=\"border: solid 1px black\">0<\/td>\n<td STYLE=\"border: solid 1px black\">0<\/td>\n<td STYLE=\"border: solid 1px black\">1<\/td>\n<td STYLE=\"border: solid 1px black\">1<\/td>\n<td STYLE=\"border: solid 1px black\">1<\/td>\n<td STYLE=\"border: solid 1px black\">1<\/td>\n<td STYLE=\"border: solid 1px black\">1<\/td>\n<td STYLE=\"border: solid 1px black\">1<\/td>\n<td STYLE=\"border: solid 1px black\">1<\/td>\n<td STYLE=\"border: solid 1px black\">1<\/td>\n<td STYLE=\"border: solid 1px black\">1<\/td>\n<td STYLE=\"border: solid 1px black\">1<\/td>\n<td STYLE=\"border: solid 1px black\">1<\/td>\n<td STYLE=\"border: solid 1px black\">1<\/td>\n<\/tr>\n<tr>\n<td COLSPAN=\"4\" STYLE=\"border: 1px black;border-style: none solid\"><code>F<\/code><\/td>\n<td COLSPAN=\"4\" STYLE=\"border: 1px black;border-style: none solid\"><code>E<\/code><\/td>\n<td COLSPAN=\"4\" STYLE=\"border: 1px black;border-style: none solid\"><code>0<\/code><\/td>\n<td COLSPAN=\"4\" STYLE=\"border: 1px black;border-style: none solid\"><code>0<\/code><\/td>\n<td COLSPAN=\"4\" STYLE=\"border: 1px black;border-style: none solid\"><code>0<\/code><\/td>\n<td COLSPAN=\"4\" STYLE=\"border: 1px black;border-style: none solid\"><code>F<\/code><\/td>\n<td COLSPAN=\"4\" STYLE=\"border: 1px black;border-style: none solid\"><code>F<\/code><\/td>\n<td COLSPAN=\"4\" STYLE=\"border: 1px black;border-style: none solid\"><code>F<\/code><\/td>\n<\/tr>\n<\/table>\n<p>The net result is therefore <\/p>\n<pre>\n     rd = rotl(ra, 5) &amp; 0xFE000FFF\n<\/pre>\n<p>The Windows debugger for PowerPC was not in use for very long, and consequently its disassembler isn&#8217;t particularly advanced. But one thing it does do for you is unpack the last two parameters and tell you what the final mask is. <\/p>\n<pre>\n10ae222c 54642d0c rlwinm  r4, r3,5,20,6            MASK=0xfe000fff\n<\/pre>\n<p>(Yes, the disassembler can&#8217;t make up its mind whether it puts a space after a comma or not. Like I said, the debugger didn&#8217;t last long enough to go through multiple iterations of polish.) <\/p>\n<p>With the <code>rlwinmi<\/code> instruction, we can build many things. (In all examples, bit index arithmetic wraps around at 32.) <\/p>\n<ul>\n<li>\n<p>    Rotate left by <var>n<\/var> bits: <\/p>\n<pre>\n    rlwinm  rd, ra, n, 0, 31\n<\/pre>\n<p>    Rotate left by <var>n<\/var> bits,     and specify a mask that starts at bit 0      and ends at bit 31.     This mask is <code>0xFFFFFFFF<\/code>,     which means that no bits get cleared. <\/p>\n<\/li>\n<li>\n<p>    Rotate right by <var>n<\/var> bits: <\/p>\n<pre>\n    rlwinm  rd, ra, 32 - n, 0, 31\n<\/pre>\n<p>    Rotate left by     32&nbsp;&minus;&nbsp;<var>n<\/var> bits,     and specify a mask that starts at bit 0      and ends at bit 31.     This mask is <code>0xFFFFFFFF<\/code>,     which means that no bits get cleared. <\/p>\n<\/li>\n<li>\n<p>    Shift left by <var>n<\/var> bits: <\/p>\n<pre>\n    rlwinm  rd, ra, n, 0, 31 - n\n<\/pre>\n<p>    Rotate left by <var>n<\/var>     and clear the rightmost <var>n<\/var> bits.     In Windows NT, the most common version of this is <\/p>\n<pre>\n    rlwinm  rd, ra, 2, 0, 29\n<\/pre>\n<p>    which shifts a value left two places.     This multiplies it by four, which is a common     operation when indexing an array of pointers. <\/p>\n<\/li>\n<li>\n<p>    Shift right by <var>n<\/var> bits: <\/p>\n<pre>\n    rlwinm  rd, ra, 32 - n, n, 31\n<\/pre>\n<p>    Rotating left by 32&nbsp;&minus;&nbsp;<var>n<\/var>     is the same as rotating right by <var>n<\/var>.     We then clear the leftmost <var>n<\/var> bits     by saying we want to keep the bits starting from     position <var>n<\/var> to the end of the register. <\/p>\n<\/li>\n<li>\n<p>    Pluck bit <var>n<\/var> out of a 32-bit value: <\/p>\n<pre>\n    rlwinm  rd, ra, n + 1, 31, 31\n<\/pre>\n<p>    Rotate left by <var>n<\/var>&nbsp;+&nbsp;1     to position the desired bit in position 31,     then clear all the other bits. <\/p>\n<\/li>\n<li>\n<p>    Extract a bitfield of length <var>m<\/var> at position <var>n<\/var>: <\/p>\n<pre>\n    rlwinm  rd, ra, n + m, 32 - m, 31\n<\/pre>\n<p>    Rotate left by <var>n<\/var>&nbsp;+&nbsp;<var>m<\/var>,     which right-aligns the field,     then clear all but the rightmost <var>m<\/var> bits. <\/p>\n<\/li>\n<li>\n<p>    Take the least significant <var>m<\/var> bits of a value     and position it so it can be inserted into a bitfield     starting at position <var>n<\/var>: <\/p>\n<pre>\n    rlwinm  rd, ra, 32 - n - m, n, n + m - 1\n<\/pre>\n<p>    Rotate right by     <var>n<\/var>&nbsp;+&nbsp;<var>m<\/var>,     which positions the field into its final location,     then clear all the bits that aren&#8217;t used to represent the field. <\/p>\n<\/li>\n<li>\n<p>    Set a bitfield     of length <var>m<\/var> at     position <var>n<\/var> to zero: <\/p>\n<pre>\n    rlwinm  rd, ra, 0, n + m, n - 1\n<\/pre>\n<p>    A rotation of zero does nothing,     but here&#8217;s where we exploit the wraparound behavior     of the mask:     We keep the bits starting at     <var>n<\/var>&nbsp;+&nbsp;<var>m<\/var>, which is the last     bit past the end of the field,     and continue keeping bits through the end of the register,     and then wrap around and keep bits starting at bit zero,     and stop at bit <var>n<\/var>&nbsp;&minus;&nbsp;1,     which is the last bit before the start of the field. <\/p>\n<\/li>\n<li>\n<p>    Zero-extend a byte to a word and do not update <var>cr0<\/var>: <\/p>\n<pre>\n    rlwinm  rd, ra, 0, 24, 31\n<\/pre>\n<p>    We perform no rotation and zero out the most significant 24 bits.     We can also do zero extension with <code>andi.<\/code>,     but the <code>rlwinm<\/code> instruction lets us do it     without updating <var>cr0<\/var>. <\/p>\n<li>\n<p>    Zero-extend a halfword to a word and do not update <var>cr0<\/var>: <\/p>\n<pre>\n    rlwinm  rd, ra, 0, 16, 31\n<\/pre>\n<p>    This time, after doing no rotation, we zero out     the most significant 16 bits.     Again, we could have done this with <code>andi.<\/code>,     but this way lets us do it without updating <var>cr0<\/var>. <\/p>\n<\/ul>\n<p>There is also a version of the instruction where the rotation amount comes from a register. <\/p>\n<pre>\n    rlwnm   rd, ra, rb, imm5a, imm5b\n    rlwnm.  rd, ra, rb, imm5a, imm5b ; also updates cr0\n<\/pre>\n<p>&#8220;Rotate left word and mask&#8221; is like &#8220;rotate left word immediate and mask&#8221;, except that the rotation amount is specified by the value of a register. (Since rotation by a multiple of 32 bits is a nop, it doesn&#8217;t matter whether bits 0 through 26 in <var>rb<\/var> are respected or ignored.) <\/p>\n<pre>\n    rlwimi  rd, ra, imm5a, imm5b, imm5c\n    rlwimi. rd, ra, imm5a, imm5b, imm5c ; also updates cr0\n<\/pre>\n<p>&#8220;Rotate left word immediate and mask insert&#8221; rotates the value in the <var>ra<\/var> register left by <var>imm5a<\/var> bits, and then copies bits <var>imm5b<\/var> through <var>imm5c<\/var> of the rotated value (wrapping around if necessary) to <var>rd<\/var>, leaving the other bits of <var>rd<\/var> alone. This instruction is most useful for storing a value into a bitfield: <\/p>\n<pre>\n    rlwimi  rd, ra, 32 - n - m, n, n + m - 1\n<\/pre>\n<p>The above instruction takes the least significant <var>m<\/var> bits of <var>ra<\/var> and sets them into a bitfield of size <var>m<\/var> starting at position <var>n<\/var> in <var>rd<\/var>. We did the math for this before, when we tried out <code>rlwinm<\/code> to position a bitfield. By using <code>rlwimi<\/code>, we get to store it. <\/p>\n<p>Okay, now we can get to the true shift instructions. <\/p>\n<pre>\n    slw     rd, ra, rb      ; rd = ra &lt;&lt; (rb % 64)\n    slw.    rd, ra, rb      ; rd = ra &lt;&lt; (rb % 64), update cr0\n\n    srw     rd, ra, rb      ; rd = ra &gt;&gt; (rb % 64)\n    srw.    rd, ra, rb      ; rd = ra &gt;&gt; (rb % 64), update cr0\n<\/pre>\n<p>The <code>slw<\/code> and <code>srw<\/code> instructions shift a register left or right by an amount specified by the value of another register. Notice that the shift amount is taken mod 64, rather than mod 32. This means that a shift by 63 will set the result to zero, but a shift by 64 will do nothing. <\/p>\n<pre>\n    sraw    rd, ra, rb      ; rd = (int32_t)ra &gt;&gt; (rb % 64), update carry\n    sraw.   rd, ra, rb      ; rd = (int32_t)ra &gt;&gt; (rb % 64), update carry and cr0\n\n    srawi   rd, ra, imm5    ; rd = (int32_t)ra &gt;&gt; imm5, update carry\n    srawi.  rd, ra, imm5    ; rd = (int32_t)ra &gt;&gt; imm5, update carry and cr0\n<\/pre>\n<p>The <code>sraw<\/code> instruction performs an arithmetic right shift by an amount specified by the <var>rb<\/var> register, and the <code>srawi<\/code> instruction does the same, but with an immediate shift amount. <\/p>\n<p>These four shift instructions are special because they always update carry: The carry bit is set if and only if the original value was negative and any bits shifted out were nonzero. This rule for the carry bit allows you to follow the right-shift instruction with an <code>addze<\/code> to perform a division by a power of two that rounds toward zero. (If you omit the <code>addze<\/code>, then the right shift performs a division by a power of two that rounds toward minus infinity.) <\/p>\n<p><b>Exercise<\/b>: How would you do a division by a power of two that rounds toward positive infinity? <\/p>\n<p>Here&#8217;s a sample code sequence to perform a C-style logical not operation: <\/p>\n<pre>\n    cmpwi   r3,0                ; set EQ if value was zero\n    mfcr    r3                  ; r3 = cr\n    rlwinm  r3,r3,eq+1,31,31    ; save the EQ bit\n<\/pre>\n<p>Similarly, you can calculate the C logical <code>&lt;<\/code> and <code>&gt;<\/code> operations by performing the comparison and extracting the <var>lt<\/var> or <var>gt<\/var> bit. To get the other three comparisons <code>!=<\/code>, <code>&lt;=<\/code> and <code>&gt;=<\/code>, you follow up with <code>xori r3, r3, 1<\/code>. <\/p>\n<p>Okay, those are the logical and shifting instructions. <a HREF=\"https:\/\/blogs.msdn.microsoft.com\/oldnewthing\/20180813-00\/?p=99475\">Next time<\/a>, we&#8217;ll look at memory access. <\/p>\n","protected":false},"excerpt":{"rendered":"<p>Get out your Swiss army knife.<\/p>\n","protected":false},"author":1069,"featured_media":111744,"comment_status":"open","ping_status":"closed","sticky":false,"template":"","format":"standard","meta":{"_acf_changed":false,"footnotes":""},"categories":[1],"tags":[2],"class_list":["post-99465","post","type-post","status-publish","format-standard","has-post-thumbnail","hentry","category-oldnewthing","tag-history"],"acf":[],"blog_post_summary":"<p>Get out your Swiss army knife.<\/p>\n","_links":{"self":[{"href":"https:\/\/devblogs.microsoft.com\/oldnewthing\/wp-json\/wp\/v2\/posts\/99465","targetHints":{"allow":["GET"]}}],"collection":[{"href":"https:\/\/devblogs.microsoft.com\/oldnewthing\/wp-json\/wp\/v2\/posts"}],"about":[{"href":"https:\/\/devblogs.microsoft.com\/oldnewthing\/wp-json\/wp\/v2\/types\/post"}],"author":[{"embeddable":true,"href":"https:\/\/devblogs.microsoft.com\/oldnewthing\/wp-json\/wp\/v2\/users\/1069"}],"replies":[{"embeddable":true,"href":"https:\/\/devblogs.microsoft.com\/oldnewthing\/wp-json\/wp\/v2\/comments?post=99465"}],"version-history":[{"count":0,"href":"https:\/\/devblogs.microsoft.com\/oldnewthing\/wp-json\/wp\/v2\/posts\/99465\/revisions"}],"wp:featuredmedia":[{"embeddable":true,"href":"https:\/\/devblogs.microsoft.com\/oldnewthing\/wp-json\/wp\/v2\/media\/111744"}],"wp:attachment":[{"href":"https:\/\/devblogs.microsoft.com\/oldnewthing\/wp-json\/wp\/v2\/media?parent=99465"}],"wp:term":[{"taxonomy":"category","embeddable":true,"href":"https:\/\/devblogs.microsoft.com\/oldnewthing\/wp-json\/wp\/v2\/categories?post=99465"},{"taxonomy":"post_tag","embeddable":true,"href":"https:\/\/devblogs.microsoft.com\/oldnewthing\/wp-json\/wp\/v2\/tags?post=99465"}],"curies":[{"name":"wp","href":"https:\/\/api.w.org\/{rel}","templated":true}]}}