{"id":1263,"date":"2019-08-10T20:47:06","date_gmt":"2019-08-10T20:47:06","guid":{"rendered":"http:\/\/retroramblings.net\/?p=1263"},"modified":"2020-02-01T15:19:24","modified_gmt":"2020-02-01T15:19:24","slug":"the-eightthirtytwo-isa","status":"publish","type":"post","link":"http:\/\/retroramblings.net\/?p=1263","title":{"rendered":"Choosing the Instruction Set"},"content":{"rendered":"\n<p><strong>The EightThirtyTwo ISA &#8211; Part 2 &#8211; 2019-08-10<\/strong><\/p>\n\n\n\n<p>In part 1 I talked about why I wanted to design my own microprocessor, and how I&#8217;d settled upon an architecture using eight-bit instruction words and eight addressable thirty-two bit registers.<\/p>\n\n\n\n<p>Because our instruction words won&#8217;t have room to encode two registers in them, each instruction will only take one operand &#8211; so we need a way of supplying a second operand to instructions such as add, xor, load immediate, etc.  For this I&#8217;ve defined a ninth register, which I&#8217;ll call temp.  This register isn&#8217;t addressable via the instruction word &#8211; it&#8217;s simply used implictly wherever it&#8217;s needed.  One of the key decisions to make will be whether the result of arithmetic instructions will go the nominated register or the temp register.<\/p>\n\n\n\n<p>It&#8217;s not entirely clear without giving the matter some thought (and ideally writing some test programs) exactly which instructions we need to implement, so initially I&#8217;ll list all the possible instructions that come to mind, and then figure out what&#8217;s mandatory, what&#8217;s optional, and what&#8217;s most beneficial in terms of keeping code size down, in the hope that we can hit our target of 25 instructions. <\/p>\n\n\n\n<!--more-->\n\n\n\n<p>Possible instructions include:<\/p>\n\n\n\n<ul class=\"wp-block-list\"><li>Load, move and store instructions:  (12 defined here)<ul><li>Load Immedate.  (LI)  Load a sign-extended six bit value into the temp register.  If the previous instruction was also Load Immediate, shift temp&#8217;s contents left six bits and &#8220;or&#8221; in the new immediate value.  This way we can build longer values with cascaded LI instructions.<\/li><li>Move to Register (MR) &#8211; move from temp to a nominated register<\/li><li>Move to Temp (MT) &#8211; move the contents of a nominated register to temp.<\/li><li>EXchanGe (EXG) &#8211; exchange the contents of the nominated register with temp.<\/li><li>Load Data (LD) &#8211; load data from RAM pointed to by the nominated register.<\/li><li>STore data (ST) &#8211; store data to RAM pointed to by the nominated register<\/li><li>Load Data and Increment &#8211; (LDINC) load data from RAM pointed to by the nominated register, and post-increment the register.<\/li><li>Load Data and Decrement &#8211; (LDDEC) load data from RAM pointed to by the nominated register, and post-increment the register.<\/li><li>STore data and Increment (STINC) &#8211; store data to RAM pointed to by the nominated register and post-increment the register.<\/li><li>STore data and Decrement (STDEC) &#8211; store data to RAM pointed to by the nominated register and post-decrement the register.<\/li><li>LoaD Byte (LDB) &#8211; Load byte from RAM pointed to by the nominated register.<\/li><li>STore Byte (STB) &#8211; Store byte from RAM pointed to by the nominated register.<\/li><\/ul><\/li><li>Control flow instructions: (11 instructions)<ul><li>JuMP (JMP) &#8211; set PC to temp<\/li><li>Jump to SubRoutine. (JSR) &#8211; push a return value on the stack, set PC to contents of TEMP.  Alternatively, set PC to temp, set temp to PC+1 and make caller responsible for saving \/ restoring.<\/li><li>RELative JuMP (RELJMP) &#8211; Add contents of temp to PC<\/li><li>RELative Jump to SubRoutine (RELJSR) &#8211; as JSR but adding tmp to PC.<\/li><li>Jump on Condition Code (JEQ, JNE, JLT, JGT, JLE, JGE) &#8211; jump conditional upon Zero and Carry flags.<\/li><li>No Operation (NOP).<\/li><\/ul><\/li><li>Arithmetic and logic instructions (19 instructions)<ul><li>Add &#8211; add temp to nominated register.  (Haven&#8217;t yet decided whether the result goes to temp or the nominated register.)<\/li><li>Add with carry (ADDC) &#8211; add temp + carry flag to nominated register.<\/li><li>Sub &#8211; subtract temp from register.<\/li><li>Reverse Sub (SUBR) &#8211; subtract register from temp.<\/li><li>Compare (CMP) &#8211; subtract temp from register, discard result.<\/li><li>Sub with carry (SUBC) &#8211; subtract temp + carry from register.<\/li><li>Negate &#8211; (NEG) contents of nominated register.<\/li><li>Multiply &#8211; (MUL)<\/li><li>And<\/li><li>Or<\/li><li>Xor<\/li><li>Not<\/li><li>Shift Left<\/li><li>Arithmetic Shift Right<\/li><li>Logical Shift Right<\/li><li>Rotate Left<\/li><li>Rotate Right<\/li><li>Rotate Left through Carry (ROLC)<\/li><li>Rotate Right through Carry (RORC)<\/li><\/ul><\/li><\/ul>\n\n\n\n<p>So that&#8217;s 42 instructions &#8211; we need to lose 17 of these.  So let&#8217;s see what can easily be implemented in terms of other instructions.<\/p>\n\n\n\n<ul class=\"wp-block-list\"><li>&#8220;Not rn&#8221; can be replaced with &#8220;li -1, xor rn&#8221;<\/li><li>&#8220;Neg rn&#8221; can be replaced with &#8220;li -1, xor rn, li 1 add rn&#8221;<\/li><li>We don&#8217;t need both rotate left and right, since the result wraps around; rotating left 8 bits is the same as rotating right 24 bits.<\/li><li>Sub could be implemented by negating one of the operands and then adding.<\/li><li>&#8220;Subr rn&#8221; could be replaced by &#8220;exch rn, sub rn&#8221;<\/li><li>Mul is nice to have but is optional for a lot of soft CPU cores.<\/li><li>The Load\/Store data with increment\/decrement instructions could be removed.<\/li><li>NOP will no doubt be needed internally for dealing with pipeline stalls and data hazards &#8211; but it&#8217;s not necessary to expose it at the ISA level. If we really want to do nothing we can use a pair of exch instructions.<\/li><\/ul>\n\n\n\n<p>That saves us 12 instructions &#8211; we need to lose another 5 so we need to think a bit further out of the box.<\/p>\n\n\n\n<p>Suppose we make the Program Counter one of the eight addressable registers: we could then implement jumps by manipulating that register, so could dispense with jmp, jsr, reljmp and reljsr.  <\/p>\n\n\n\n<p>The conditional jump instruction could remain if we can encode the conditions into three bits &#8211; but another option, and one that&#8217;s quite appealing, is to borrow an idea that goes back to the 1950s, but which has gone out of fashion in recent years: Predication.<\/p>\n\n\n\n<p>Instruction sets with predication generally devote a few bits to the instruction encoding for condition flags which will allow or prevent each instruction executing on an individual level depending upon whether they match the CPU&#8217;s current condition codes.  PA-RISC, IA-64 and ARM32 have comprehensive predication, but MIPS and x86 are basically limited to conditional branches and a Conditional Move instruction.<\/p>\n\n\n\n<p>Obviously we can&#8217;t devote encoding space to full predication, but what we could do is define a &#8220;Cond&#8221; instruction, which will match against condition codes, then enable or disable execution of all instructions until further notice.  The result could look like this<\/p>\n\n\n\n<pre class=\"wp-block-preformatted\">cmp r0<br>cond SGT ; only execute the following instructions if temp was strictly greater than r0<br>li branchtarget<br>mv r7 ; r7 is the program counter.<br>cond EX ; Go back to unconditional execution<\/pre>\n\n\n\n<p>The appealing thing about this is that we could implement simple &#8220;If&#8230;else&#8230;&#8221; control flow without needing to branch at all.  <\/p>\n\n\n\n<p>Next time I&#8217;ll write some more code snippets, see how this ISA feels, decide which instructions to keep and whether any simple changes can be made to make it more comfortable to use.<\/p>\n","protected":false},"excerpt":{"rendered":"<p>The EightThirtyTwo ISA &#8211; Part 2 &#8211; 2019-08-10 In part 1 I talked about why I wanted to design my own microprocessor, and how I&#8217;d settled upon an architecture using eight-bit instruction words and eight addressable thirty-two bit registers. Because &hellip; <a href=\"http:\/\/retroramblings.net\/?p=1263\">Continue reading <span class=\"meta-nav\">&rarr;<\/span><\/a><\/p>\n","protected":false},"author":2,"featured_media":0,"comment_status":"open","ping_status":"closed","sticky":false,"template":"","format":"standard","meta":{"footnotes":""},"categories":[12,4,8],"tags":[],"class_list":["post-1263","post","type-post","status-publish","format-standard","hentry","category-eightthirtytwo","category-fpga","category-hardware"],"_links":{"self":[{"href":"http:\/\/retroramblings.net\/index.php?rest_route=\/wp\/v2\/posts\/1263","targetHints":{"allow":["GET"]}}],"collection":[{"href":"http:\/\/retroramblings.net\/index.php?rest_route=\/wp\/v2\/posts"}],"about":[{"href":"http:\/\/retroramblings.net\/index.php?rest_route=\/wp\/v2\/types\/post"}],"author":[{"embeddable":true,"href":"http:\/\/retroramblings.net\/index.php?rest_route=\/wp\/v2\/users\/2"}],"replies":[{"embeddable":true,"href":"http:\/\/retroramblings.net\/index.php?rest_route=%2Fwp%2Fv2%2Fcomments&post=1263"}],"version-history":[{"count":6,"href":"http:\/\/retroramblings.net\/index.php?rest_route=\/wp\/v2\/posts\/1263\/revisions"}],"predecessor-version":[{"id":1341,"href":"http:\/\/retroramblings.net\/index.php?rest_route=\/wp\/v2\/posts\/1263\/revisions\/1341"}],"wp:attachment":[{"href":"http:\/\/retroramblings.net\/index.php?rest_route=%2Fwp%2Fv2%2Fmedia&parent=1263"}],"wp:term":[{"taxonomy":"category","embeddable":true,"href":"http:\/\/retroramblings.net\/index.php?rest_route=%2Fwp%2Fv2%2Fcategories&post=1263"},{"taxonomy":"post_tag","embeddable":true,"href":"http:\/\/retroramblings.net\/index.php?rest_route=%2Fwp%2Fv2%2Ftags&post=1263"}],"curies":[{"name":"wp","href":"https:\/\/api.w.org\/{rel}","templated":true}]}}