{"id":70,"date":"2025-02-12T07:15:24","date_gmt":"2025-02-12T07:15:24","guid":{"rendered":"https:\/\/www.soapyfrog.com\/?p=70"},"modified":"2025-02-12T15:27:24","modified_gmt":"2025-02-12T15:27:24","slug":"synthetic-cpu-first-tests","status":"publish","type":"post","link":"https:\/\/www.soapyfrog.com\/index.php\/2025\/02\/12\/synthetic-cpu-first-tests\/","title":{"rendered":"Synthetic CPU first tests"},"content":{"rendered":"\n<p class=\"wp-block-paragraph\">In the last post, <a href=\"https:\/\/www.soapyfrog.com\/index.php\/2025\/01\/23\/building-the-synthetic-cpu\/\" data-type=\"post\" data-id=\"60\">Building the synthetic CPU<\/a>, I expected the CPU, running a <em>machine code<\/em> version of the Mandelbrot test, to run at least 10x faster.<\/p>\n\n\n\n<p class=\"wp-block-paragraph\">Well, here&#8217;s the test:<\/p>\n\n\n\n<figure class=\"wp-block-image size-large\"><img loading=\"lazy\" decoding=\"async\" width=\"1024\" height=\"992\" src=\"https:\/\/www.soapyfrog.com\/wp-content\/uploads\/2025\/02\/mandel-cpu-screenshot-1024x992.png\" alt=\"\" class=\"wp-image-73\" srcset=\"https:\/\/www.soapyfrog.com\/wp-content\/uploads\/2025\/02\/mandel-cpu-screenshot-1024x992.png 1024w, https:\/\/www.soapyfrog.com\/wp-content\/uploads\/2025\/02\/mandel-cpu-screenshot-300x291.png 300w, https:\/\/www.soapyfrog.com\/wp-content\/uploads\/2025\/02\/mandel-cpu-screenshot-768x744.png 768w, https:\/\/www.soapyfrog.com\/wp-content\/uploads\/2025\/02\/mandel-cpu-screenshot-1536x1488.png 1536w, https:\/\/www.soapyfrog.com\/wp-content\/uploads\/2025\/02\/mandel-cpu-screenshot.png 1668w\" sizes=\"auto, (max-width: 1024px) 100vw, 1024px\" \/><\/figure>\n\n\n\n<p class=\"wp-block-paragraph\">It ran in <em>4ms<\/em>, and the original, SoapyBASIC interpreted version runs in about 50ms.<\/p>\n\n\n\n<p class=\"wp-block-paragraph\">So I&#8217;m happy with that.<\/p>\n\n\n\n<p class=\"wp-block-paragraph\">My synthetic CPU does an iterative decode of the instruction from memory and execute in one pass, so there&#8217;s no parallel decode\/execute and no caching of decoded instructions. <\/p>\n\n\n\n<p class=\"wp-block-paragraph\">I tried decoding instructions and producing closures (this is in Swift) that execute in a chain, but the overheads made it quite slow.<\/p>\n\n\n\n<p class=\"wp-block-paragraph\">If I choose to move this to C that&#8217;s what I&#8217;d do though; a kind of pseudo-JIT.<\/p>\n\n\n\n<p class=\"wp-block-paragraph\">That&#8217;s all for this post. The next steps are completing the last 15% or so instructions not handled and writing a mini assembler (the Mandelbrot code producing the above was hand assembled \ud83d\ude41 ).<\/p>\n\n\n\n<p class=\"wp-block-paragraph\">Below is that assembly code. As you can see it&#8217;s a bit ARM\/RISC-V like and although there is no memory access in here, it is a load\/store architecture and every instruction is 32-bits. 32 each of general purpose and floating point registers.<\/p>\n\n\n\n<p class=\"wp-block-paragraph\">More on this in future articles.<\/p>\n\n\n\n<pre class=\"wp-block-code\"><code>  fset fs0, 79.0     ; num lines\n  fset fs1, 30.0     ; num cols\n  set s0, 16         ; max iter\n  fset fs2, -2.0     ; minRe\n  fset fs3, 1.0      ; maxRe\n  fset fs4, -1.0     ; minIm\n  fset fs5, 1.0      ; maxIm\n\n  ; fs6 (imstep) = (maxIm-minIm)\/lines\n  fsub fs6, fs5, fs4\n  fdiv fs6, fs6, fs0\n\n  ; fs7 (restep) = (maxRe-minRe)\/cols\n  fsub fs7, fs3, fs2\n  fdiv fs7, fs7, fs1\n\n  ; for im(fs8) = minIm to maxIm step imstep\n  fcopy fs8, fs4\nimloop:\n  ; for re(fs9) = minRe to maxRe step restep\n  fcopy fs9, fs2\nreloop:\n  fcopy fs10, fs9 ; zr(fs10)=re\n  fcopy fs11, fs8 ; zi(fs11)=im\n  ; while n (s1) &lt; maxIter\n  set s1, 0\nnloop:\n  sub zero, s1, s0 ; zero = n-maxIter\n  ; above could be cmp s1, s0 pseudo-instruction?\n  b_eq nbreak\n  ; a(fs12)=zr*zr, b(fs13)=zi*zi\n  fmul fs12, fs10, fs10\n  fmul fs13, fs11, fs11\n  ; if a+b &gt; 4 break\n  fadd ft0, fs12, fs13\n  fset ft1, 4.0\n  fcmp ft0, ft1\n  b_gt nbreak\n  ; zi = 2*zr*zi+im\n  fset ft0, 2.0\n  fmul ft0, fs10, ft0\n  fmul ft0, fs11, ft0\n  fadd fs11, ft0, fs8\n  ; zr = a-b+re\n  fsub ft0, fs12, fs13\n  fadd fs10, ft0, fs9\n  ; next n\n  add s1, s1, 1\n  b nloop\nnbreak:\n  ; print char n\n  copy a0, s1 a0=n\n  syscall 2\n  ; next re(fs9)\n  fadd fs9, fs9, fs7\n  fcmp fs9, fs3 ; re,maxRe\n  b_le reloop\n  ; print new line\n  set a0, 10\n  syscall 1\n  ; next im(fs8)\n  fadd fs8, fs8, fs7\n  fcmp fs8, fs5 ; im,maxIm\n  b_le imloop\n\n  halt\n<\/code><\/pre>\n","protected":false},"excerpt":{"rendered":"<p>In the last post, Building the synthetic CPU, I expected the CPU, running a machine code version of the Mandelbrot test, to run at least 10x faster. Well, here&#8217;s the test: It ran in 4ms, and the original, SoapyBASIC interpreted version runs in about 50ms. So I&#8217;m happy with that. My synthetic CPU does an [&hellip;]<\/p>\n","protected":false},"author":1,"featured_media":74,"comment_status":"open","ping_status":"open","sticky":false,"template":"","format":"standard","meta":{"footnotes":""},"categories":[14,13,15],"tags":[21,19,18,20],"class_list":["post-70","post","type-post","status-publish","format-standard","has-post-thumbnail","hentry","category-lang-design","category-soapybasic","category-virtual-machines","tag-instruction-set-architecture","tag-language-design","tag-retrocomputer","tag-swift-lang"],"_links":{"self":[{"href":"https:\/\/www.soapyfrog.com\/index.php\/wp-json\/wp\/v2\/posts\/70","targetHints":{"allow":["GET"]}}],"collection":[{"href":"https:\/\/www.soapyfrog.com\/index.php\/wp-json\/wp\/v2\/posts"}],"about":[{"href":"https:\/\/www.soapyfrog.com\/index.php\/wp-json\/wp\/v2\/types\/post"}],"author":[{"embeddable":true,"href":"https:\/\/www.soapyfrog.com\/index.php\/wp-json\/wp\/v2\/users\/1"}],"replies":[{"embeddable":true,"href":"https:\/\/www.soapyfrog.com\/index.php\/wp-json\/wp\/v2\/comments?post=70"}],"version-history":[{"count":3,"href":"https:\/\/www.soapyfrog.com\/index.php\/wp-json\/wp\/v2\/posts\/70\/revisions"}],"predecessor-version":[{"id":77,"href":"https:\/\/www.soapyfrog.com\/index.php\/wp-json\/wp\/v2\/posts\/70\/revisions\/77"}],"wp:featuredmedia":[{"embeddable":true,"href":"https:\/\/www.soapyfrog.com\/index.php\/wp-json\/wp\/v2\/media\/74"}],"wp:attachment":[{"href":"https:\/\/www.soapyfrog.com\/index.php\/wp-json\/wp\/v2\/media?parent=70"}],"wp:term":[{"taxonomy":"category","embeddable":true,"href":"https:\/\/www.soapyfrog.com\/index.php\/wp-json\/wp\/v2\/categories?post=70"},{"taxonomy":"post_tag","embeddable":true,"href":"https:\/\/www.soapyfrog.com\/index.php\/wp-json\/wp\/v2\/tags?post=70"}],"curies":[{"name":"wp","href":"https:\/\/api.w.org\/{rel}","templated":true}]}}