{"id":443,"date":"2013-04-22T19:38:55","date_gmt":"2013-04-22T19:38:55","guid":{"rendered":"http:\/\/retroramblings.net\/?p=443"},"modified":"2026-08-14T12:56:44","modified_gmt":"2026-08-14T12:56:44","slug":"a-closer-look-at-some-zpu-instructions","status":"publish","type":"post","link":"https:\/\/retroramblings.net\/?p=443","title":{"rendered":"A closer look at some ZPU instructions"},"content":{"rendered":"<p>In my last post I outlined a very simple test program written in C and showed the ZPU assembly language that the ZPU GCC toolchain creates.<\/p>\n<p>Let&#8217;s take a closer look at the program and see what&#8217;s going on.<br \/>\n<!--more--><br \/>\nAs I mentioned, the ZPU doesn&#8217;t have a traditional register file &#8211; instead it has a stack, similar to the way languages such as Forth and Postscript work.\u00a0 What I&#8217;ll do here is go through the test program line-by-line and analyze what&#8217;s going on:<\/p>\n<pre>main:\r\n\u00a0\u00a0 \u00a0im -1<\/pre>\n<p>This pushes an immediate value, in this case -1, to the stack.\u00a0 The im instruction is interesting; its operand is only 7 bits, but if two im instructions follow each other, the second shifts the value on top of the stack 7 bits left and puts its own operand in the low 7 bits.\u00a0 This means that loading a full 32-bit value requires five consecutive im instructions!\u00a0 That&#8217;s not actually anywhere near as nuts as it might sound:\u00a0 Firstly any RISC architecture with an instruction length of 32-bits or less requires more than one instruction to load an immediate 32-bit value.\u00a0 Since the ZPU&#8217;s opcodes are only a single byte long, loading a 32-bit immediate requires five bytes of code, while a CPU with a 32-bit instruction size would require eight!\u00a0 Secondly, the first im instruction is sign-extended across the whole word, so values at either end of the 32-bit range can be loaded with significantly fewer than 5 instructions.\u00a0 This is why I chose 0xffffff80 as the address of my hardware register in the test program &#8211; that address can be loaded in two instructions.\u00a0 0xffffffc0 would have been a better choice still; it can be loaded in just one.<br \/>\nStack contents after this instruction has completed: &lt;-1&gt; &#8230;<\/p>\n<pre>pushspadd<\/pre>\n<p>Pops the top value off the stack, and adds it to the stack pointer, pushing the result.\u00a0 (Pushspadd is an emulated instruction in the small core, so is slow.)<br \/>\nStack contents: &lt;old stack pointer -1&gt; &#8230;<\/p>\n<pre>popsp<\/pre>\n<p>Use the top value from the stack as the new stack pointer<br \/>\nStack contents: &lt;free space&gt; &lt;free space&gt; &#8230;<br \/>\nThe above code has basically allocated space on the stack for two words &#8211; which is not amazingly useful &#8211; you could achieve the same thing with &#8220;im 0; nop; im 0&#8221; &#8211; but it makes more sense with larger allocations.<\/p>\n<pre>im 0\r\nstoresp 8<\/pre>\n<p>This pushes the immediate value 0, then writes it to the stack, 8 bytes (2 words) in.<br \/>\nStack contents before storesp:\u00a0 &lt;0&gt; &lt;free space&gt; &lt;free space&gt; &#8230;<br \/>\nStack contents after storesp: &lt;free space&gt; &lt;0&gt; &#8230;<\/p>\n<pre>.L2:\r\nloadsp 4<\/pre>\n<p>Copies the value four bytes (1 word) into the stack, and pushes it to the top.<br \/>\nStack contents: &lt;0&gt; &lt;free space&gt; &lt;0&gt; &#8230;<\/p>\n<pre>im 1\r\naddsp 12<\/pre>\n<p>Adds 1 to the value 12 bytes into the stack, leaving the result on top of the stack.<\/p>\n<pre>storesp 12<\/pre>\n<p>Stores the result of the previous addition 12 bytes into the stack.<br \/>\nStack contents: &lt;0&gt; &lt;free space&gt; &lt;1&gt; &#8230;<\/p>\n<pre>im -128<\/pre>\n<p>stack contents: &lt;0xffffff80&gt; &lt;0&gt; &lt;free space&gt; &lt;1&gt; &#8230;<\/p>\n<pre>store<\/pre>\n<p>Pops two values from the stack, and writes the second value to the address pointed to by the first.<br \/>\nStack contents: &lt;free space&gt; &lt;1&gt; ,,,<\/p>\n<pre>loadsp 4\r\nim 1\r\naddsp 12\r\nstoresp 12\r\nim -128\r\nstore<\/pre>\n<p>This is a repeat of the previous six instructions &#8211; GCC has unrolled the loop<br \/>\nStack contents: &lt;free space&gt; &lt;2&gt; &#8230;<\/p>\n<pre>impcrel .L2\r\npoppcrel<\/pre>\n<p>These two instructions form a PC-relative &#8220;jump&#8221; instruction.\u00a0 impcrel is more of a compiler directive than a true instruction, and poppcrel is an emulated instruction, so is slow on the small version of the core.<\/p>\n<p>There are some compiler flags we can use to tell GCC to avoid emulated instructions &#8211; this can make a big difference to speed, and in situations where code space is tight, can remove the need to include the emulation code.<\/p>\n<pre>CFLAGS= -mno-poppcrel -mno-compare -mno-eq -mno-byteop -mno-shortop -mno-callpcrel \\\r\n  -mno-call -mno-lshiftrt -mno-ashiftl -mno-ashiftrt -mno-neqbranch -mno-pushspadd \\\r\n  -mno-neg -mno-mod -mno-div -mno-mult<\/pre>\n<p>These flags will cause the GCC-generated code to avoid the use of the named instructions. One of the things I want to try is to see which of these I can include in the small core while still keeping the core under 1,000 logic elements.<\/p>\n","protected":false},"excerpt":{"rendered":"<p>In my last post I outlined a very simple test program written in C and showed the ZPU assembly language that the ZPU GCC toolchain creates. Let&#8217;s take a closer look at the program and see what&#8217;s going on.<\/p>\n","protected":false},"author":2,"featured_media":0,"comment_status":"closed","ping_status":"closed","sticky":false,"template":"","format":"standard","meta":{"footnotes":""},"categories":[4],"tags":[],"class_list":["post-443","post","type-post","status-publish","format-standard","hentry","category-fpga"],"_links":{"self":[{"href":"https:\/\/retroramblings.net\/index.php?rest_route=\/wp\/v2\/posts\/443","targetHints":{"allow":["GET"]}}],"collection":[{"href":"https:\/\/retroramblings.net\/index.php?rest_route=\/wp\/v2\/posts"}],"about":[{"href":"https:\/\/retroramblings.net\/index.php?rest_route=\/wp\/v2\/types\/post"}],"author":[{"embeddable":true,"href":"https:\/\/retroramblings.net\/index.php?rest_route=\/wp\/v2\/users\/2"}],"replies":[{"embeddable":true,"href":"https:\/\/retroramblings.net\/index.php?rest_route=%2Fwp%2Fv2%2Fcomments&post=443"}],"version-history":[{"count":11,"href":"https:\/\/retroramblings.net\/index.php?rest_route=\/wp\/v2\/posts\/443\/revisions"}],"predecessor-version":[{"id":1086,"href":"https:\/\/retroramblings.net\/index.php?rest_route=\/wp\/v2\/posts\/443\/revisions\/1086"}],"wp:attachment":[{"href":"https:\/\/retroramblings.net\/index.php?rest_route=%2Fwp%2Fv2%2Fmedia&parent=443"}],"wp:term":[{"taxonomy":"category","embeddable":true,"href":"https:\/\/retroramblings.net\/index.php?rest_route=%2Fwp%2Fv2%2Fcategories&post=443"},{"taxonomy":"post_tag","embeddable":true,"href":"https:\/\/retroramblings.net\/index.php?rest_route=%2Fwp%2Fv2%2Ftags&post=443"}],"curies":[{"name":"wp","href":"https:\/\/api.w.org\/{rel}","templated":true}]}}