{"id":893,"date":"2018-08-30T13:10:57","date_gmt":"2018-08-30T21:10:57","guid":{"rendered":"http:\/\/www.adamantyr.com\/blog\/?p=893"},"modified":"2018-08-31T08:20:44","modified_gmt":"2018-08-31T16:20:44","slug":"random-thoughts","status":"publish","type":"post","link":"http:\/\/www.adamantyr.com\/index.php\/2018\/08\/30\/random-thoughts\/","title":{"rendered":"Random Thoughts"},"content":{"rendered":"<p>And now a side-trek on random number generation&#8230;<\/p>\n<p>Pseudo-random numbers have been a part of software design for awhile. It&#8217;s a field that is still very heavy in the mathematics area, because a large part of it is about trying to design an algorithm to create a random value quickly but also predictably.<\/p>\n<p>However, if you started with BASIC in the old days, all that was hidden from you. You just called a RAND or RND function and got a number. The better BASIC languages offered a way to change or update the random seed, but otherwise you just used what they gave you and didn&#8217;t know what it did under the hood.<\/p>\n<p>For most 99&#8217;ers, the earliest best example of an assembly random number generator came from the source code for <em>Tombstone City<\/em>, a cartridge game from Texas Instruments. The source code was provided with the Editor\/Assembler package as a learning tool. (Sadly, as a teenager, I wasn&#8217;t aware of this. Studying source code may have made my early frustrations with assembly programming easier. Ah well&#8230;)<\/p>\n<p>Here is the routine from it:<\/p>\n<pre>RANDNO LI   R4,28645\r\n       MPY  @RAND16,R4\r\n       AI   R5,31417\r\n       MOV  R5,@RAND16\r\n       RT\r\n<\/pre>\n<p>As you can see, not a lot to this one. Let&#8217;s say you started with a random seed of 0 and went a few cycles:<\/p>\n<ul>\n<li>0 * 28645 = 0 + 31417 = 31417<\/li>\n<li>31417 * 28645 = 65149 (low word) + 31417 = 31030<\/li>\n<li>31030 * 28645 = 55118 (low word) + 31417 = 20999<\/li>\n<li>20999 * 28645 = 26947 (low word) + 31417 = 58364<\/li>\n<\/ul>\n<p>This is pretty much what every pseudo-random algorithm does. It generates a sequence of values using the prior value as a key to generate the new value. It also creates a predictable sequence, which can be useful in situations where you want the randomness to be more controlled. It also produces an exact sequence of 65536 different values; changing the seed value just changes where you start in the sequence.<\/p>\n<p>However, it consistently produces an even\/odd pattern. In some cases, this may be desirable! But for most random routines you want the even\/oddness to be variant.<\/p>\n<p>A simple fix, which is what I introduced and it did the trick at the time, was to throw in a bit shift of an odd value on the random seed, as follows:<\/p>\n<pre>RANDNO LI   R4,28645\r\n       MPY  @RAND16,R4\r\n       AI   R5,31417\r\n       SRC  R5,5\r\n       MOV  R5,@RAND16\r\n       RT\r\n<\/pre>\n<p>By shifting the bits an odd number of times it broke up the even\/odd cycle. It however still creates a predictable sequence since you are always shifting by a predictable amount.<\/p>\n<p>So my next solution was to have the bit shift be determined by an outside factor.<\/p>\n<p>The TI-99\/4a has a vertical sync clock which ticks at 1\/60 of a second. By using that value to determine the bit shift, I was able to have it shift to a completely different place in the sequence in an unpredictable fashion:<\/p>\n<pre>RANDNO LI   R4,28645\r\n       MPY  @RAND16,R4\r\n       AI   R5,31417\r\n       MOV  @CLOCK,R0\r\n       ANDI R0,&gt;000F\r\n       SRC  R5,0\r\n       MOV  R5,@RAND16\r\n       RT\r\n<\/pre>\n<p>@CLOCK in this instance is a word that I increment every time there is a vertical sync.<\/p>\n<p>But even THIS one is not perfect&#8230; because when I tried to use it to generate random map content, which involved consecutive calls to the random function in a tight time frame when the vertical clock did not actually change, it created strange symmetric patterns every 4th or 5th iteration.<\/p>\n<p>So I decided to change the routine, for the map generation at least, to a different approach:<\/p>\n<pre>MASK   DATA &gt;B400\r\nRANDNO MOV  @RAND16,R0\r\n       SRL  R0,1\r\n       JNC  NOCARR\r\n       XOR  @MASK,R0\r\nNOCARR MOV  R0,@RAND16<\/pre>\n<p>This rather clever routine avoids the use of multiplication (which is unavailable in many assembly languages of the era, and is cycle-costly) and uses XOR instead to invert the bits when there is no carry. This creates a predictable sequence that varies high and low.<\/p>\n<p>Let&#8217;s step through it a bit, starting with 0:<\/p>\n<ul>\n<li>0 &gt;&gt; 1 = 0, No carry, XOR &gt;B400 = 46080<\/li>\n<li>46080 &gt;&gt; 1 = 23040, No carry, XOR &gt;B400 = 60928<\/li>\n<li>60928 &gt;&gt; 1 = 30464, No carry, XOR &gt;B400 = 49920<\/li>\n<li>49920 &gt;&gt; 1 = 24960, No carry, XOR &gt;B400 = 54656<\/li>\n<\/ul>\n<p>So this one worked better, since it wasn&#8217;t relying upon the clock value. Until I reached a particular map where I was placing random tiles in a smaller area. Suddenly every single map produced the EXACT SAME pattern of tiles in an odd diagonal line. I realized then that I had to go back to the drawing board.<\/p>\n<p>The solution (at present anyway) is to use a different clock. I needed one that counted faster than 1\/60 of a second, and wasn&#8217;t reliant upon the vertical sync, which requires you to have interrupts enabled to update the value.<\/p>\n<p>I found one in the interface chip the TMS9901, which has a 14-bit counter value you can set. It decrements it at clock-cycle speed so it&#8217;s VERY fast. It requires a bit of setup in your program to initialize with a value, but after that it&#8217;s readily accessible. After I put this in, my random values are EXTREMELY random and satisfying.<\/p>\n<pre>* Somewhere at program start\r\n* Set up 9901 counter\r\n       CLR  R12             * CRU base of the TMS9901\r\n       SBO  0               * Enter timer mode\r\n       LI   R1,&gt;000F        * Set counter to 15\r\n       INCT R12             * Address of bit 1\r\n       LDCR R1,14           * Load value\r\n       DECT R12\r\n       SBZ  0               * Exit clock mode, start decrementer\r\n* Random function\r\nRANDNO LI   R4,23729        * Load with 15-bit value\r\n       MPY  @RSEED,R4       * Multiply by the random seed\r\n       AI   R5,31871        * Add a 15-bit value to the lower word\r\n       CLR  R12             * CRU base of the TMS9901\r\n       SBO  0               * Enter timer mode\r\n       STCR R0,15           * Read current value (plus mode bit)\r\n       SRL  R0,1            * Get rid of mode bit\r\n       SBZ  0               * Exit clock mode, decrementer continues\r\n       SRC  R5,0            * Rotate seed based on clock value\r\n       MOV  R5,@RSEED       * Copy back to RSEED\r\n       RT\r\n<\/pre>\n<p>Alternatively, if I was coding in a system that lacked such a handy counter, I would try and create some kind of clock that is incremented by other parts of my program that I could use in it&#8217;s place. The most important aspect of it is that it can&#8217;t be in the random function itself!<\/p>\n<p>One other option, that occurred to me afterwards, is that I could have opened the interrupts on the TI during the random function so that the clock value would get incremented. I&#8217;m reluctant to do this, though, because in many places I&#8217;m doing more during interrupts than just a clock, and they would steal resources away from map generation.<\/p>\n<p>So for now, I seem to have enough randomness to use everywhere in the CRPG. \ud83d\ude42<\/p>\n","protected":false},"excerpt":{"rendered":"<p>And now a side-trek on random number generation&#8230; Pseudo-random numbers have been a part of software design for awhile. It&#8217;s a field that is still very heavy in the mathematics area, because a large part of it is about trying &hellip; <a href=\"http:\/\/www.adamantyr.com\/index.php\/2018\/08\/30\/random-thoughts\/\">Continue reading <span class=\"meta-nav\">&rarr;<\/span><\/a><\/p>\n","protected":false},"author":1,"featured_media":0,"comment_status":"open","ping_status":"open","sticky":false,"template":"","format":"standard","meta":{"_jetpack_newsletter_access":"","_jetpack_dont_email_post_to_subs":false,"_jetpack_newsletter_tier_id":0,"_jetpack_memberships_contains_paywalled_content":false,"_jetpack_memberships_contains_paid_content":false,"footnotes":""},"categories":[16,4,5,12],"tags":[],"class_list":["post-893","post","type-post","status-publish","format-standard","hentry","category-assembly","category-crpg","category-design","category-ti-994a"],"jetpack_featured_media_url":"","jetpack_sharing_enabled":true,"jetpack_shortlink":"https:\/\/wp.me\/pgaeMJ-ep","_links":{"self":[{"href":"http:\/\/www.adamantyr.com\/index.php\/wp-json\/wp\/v2\/posts\/893","targetHints":{"allow":["GET"]}}],"collection":[{"href":"http:\/\/www.adamantyr.com\/index.php\/wp-json\/wp\/v2\/posts"}],"about":[{"href":"http:\/\/www.adamantyr.com\/index.php\/wp-json\/wp\/v2\/types\/post"}],"author":[{"embeddable":true,"href":"http:\/\/www.adamantyr.com\/index.php\/wp-json\/wp\/v2\/users\/1"}],"replies":[{"embeddable":true,"href":"http:\/\/www.adamantyr.com\/index.php\/wp-json\/wp\/v2\/comments?post=893"}],"version-history":[{"count":2,"href":"http:\/\/www.adamantyr.com\/index.php\/wp-json\/wp\/v2\/posts\/893\/revisions"}],"predecessor-version":[{"id":895,"href":"http:\/\/www.adamantyr.com\/index.php\/wp-json\/wp\/v2\/posts\/893\/revisions\/895"}],"wp:attachment":[{"href":"http:\/\/www.adamantyr.com\/index.php\/wp-json\/wp\/v2\/media?parent=893"}],"wp:term":[{"taxonomy":"category","embeddable":true,"href":"http:\/\/www.adamantyr.com\/index.php\/wp-json\/wp\/v2\/categories?post=893"},{"taxonomy":"post_tag","embeddable":true,"href":"http:\/\/www.adamantyr.com\/index.php\/wp-json\/wp\/v2\/tags?post=893"}],"curies":[{"name":"wp","href":"https:\/\/api.w.org\/{rel}","templated":true}]}}