<?xml version="1.0" encoding="utf-8"?><feed xmlns="http://www.w3.org/2005/Atom" ><generator uri="https://jekyllrb.com/" version="3.10.0">Jekyll</generator><link href="http://www.richardfawcett.net/feed.xml" rel="self" type="application/atom+xml" /><link href="http://www.richardfawcett.net/" rel="alternate" type="text/html" /><updated>2025-06-19T03:43:37+00:00</updated><id>http://www.richardfawcett.net/feed.xml</id><title type="html">Richard Fawcett’s website</title><subtitle>Personal website of Richard Fawcett.</subtitle><entry><title type="html">Static Site Generation in 2025 - Gatsby</title><link href="http://www.richardfawcett.net/2025/06/13/static-site-generation-in-2025-gatsby" rel="alternate" type="text/html" title="Static Site Generation in 2025 - Gatsby" /><published>2025-06-13T00:00:00+00:00</published><updated>2025-06-13T00:00:00+00:00</updated><id>http://www.richardfawcett.net/2025/06/13/static-site-generation-in-2025-gatsby</id><content type="html" xml:base="http://www.richardfawcett.net/2025/06/13/static-site-generation-in-2025-gatsby"><![CDATA[<p>Following on from the previous post about static site generators, the next one I took for a spin was Gatsby.</p>

<p>Installing via <code class="language-plaintext highlighter-rouge">npm init gatsby</code> is a great experience. This will automatically download the <code class="language-plaintext highlighter-rouge">create-gatsby</code> package and executed it, meaning there’s no need to install anything prior to development, other than node and npm.  This init script guides us through a terminal user interface (TUI) to select various options, such as project name, whether we want to use a CMS, and some optional plugins we might want to enable.  It finishes by suggesting we run <code class="language-plaintext highlighter-rouge">npm run develop</code> to launch our newly created site.</p>

<p>And that’s where the wheels come off a little bit.  I’m running node version 22.11.0, and the first run gives an error:</p>

<p><img src="/assets/images/20250613_gatsby/gatsby-error.png" alt="Gatsby error" title="Gatsby error" /></p>

<p>Apparently, the solution is to downgrade node to version 20, but this workaround won’t last forever, and the bug was <a href="https://github.com/gatsbyjs/gatsby/issues/39019">first reported</a> a year ago. Node 22 is the current LTS release, so having to downgrade is disappointing.  I feel it’s not a great first run experience, but it appears the “error” is benign (which begs the question, why not a warning?)  I also appreciate that this is a transitive dependency that Gatsby takes, so it might not be in their hands to fix … I didn’t have time to dig into whether Gatsby’s direct dependencies have been patched to fix this.  But overall, I think that if a user is running the current LTS and encounters this, it should be at least called out in a fairly obvious way on the Gatsby quick start guide.</p>

<p><strong>UPDATE</strong> - after writing the above, based on my use of Gatsby’s <a href="https://www.gatsbyjs.com/docs/quick-start/">quick start</a>, I took a look at the full tutorial; <a href="https://www.gatsbyjs.com/docs/tutorial/getting-started/part-0/">section 0</a> does say that the requirements are for Node.js (v18 or newer, but less than v21).  Still disappointing, but less so.  I feel this should be more prominently called out, including on the quick start guide. Further down the section 0 page, it says to install node with <code class="language-plaintext highlighter-rouge">brew install node</code> which would not meet the requirements … at the time of writing, this will install Node v24.  It likely should say to use <code class="language-plaintext highlighter-rouge">brew install node@20</code>.</p>

<h3 id="using-react">Using React</h3>

<p>The getting started guide then took me into writing <code class="language-plaintext highlighter-rouge">.js</code> pages to show pages.  This is a much steeper learning curve than just dropping Markdown files in a directory like with Hugo.  For example, to create an About page, the code below is suggested as a starting point and should be saved at <code class="language-plaintext highlighter-rouge">src/pages/about.js</code>:</p>

<div class="language-javascript highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="c1">// Step 1: Import React</span>
<span class="k">import</span> <span class="o">*</span> <span class="k">as</span> <span class="nx">React</span> <span class="k">from</span> <span class="dl">'</span><span class="s1">react</span><span class="dl">'</span>

<span class="c1">// Step 2: Define your component</span>
<span class="kd">const</span> <span class="nx">AboutPage</span> <span class="o">=</span> <span class="p">()</span> <span class="o">=&gt;</span> <span class="p">{</span>
	<span class="k">return</span> <span class="p">(</span>
		<span class="o">&lt;</span><span class="nx">main</span><span class="o">&gt;</span>
			<span class="o">&lt;</span><span class="nx">h1</span><span class="o">&gt;</span><span class="nx">About</span> <span class="nx">me</span><span class="o">&lt;</span><span class="sr">/h1</span><span class="err">&gt;
</span>			<span class="o">&lt;</span><span class="nx">p</span><span class="o">&gt;</span><span class="nx">Hi</span> <span class="nx">there</span><span class="o">!</span> <span class="nx">I</span><span class="dl">'</span><span class="s1">m Richard.&lt;/p&gt;
		&lt;/main&gt;
	)
}

export const Head = () =&gt; &lt;title&gt;About Me&lt;/title&gt;

// Step 3: Export your component
export default AboutPage
</span></code></pre></div></div>

<p>The about page can then be found under <code class="language-plaintext highlighter-rouge">/about</code> in the generated site.</p>

<h3 id="what-about-layouts">What About Layouts?</h3>

<p>Layouts can be implemented too, e.g.:</p>

<div class="language-javascript highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="k">import</span> <span class="o">*</span> <span class="k">as</span> <span class="nx">React</span> <span class="k">from</span> <span class="dl">'</span><span class="s1">react</span><span class="dl">'</span>
<span class="k">import</span> <span class="p">{</span> <span class="nx">Link</span> <span class="p">}</span> <span class="k">from</span> <span class="dl">'</span><span class="s1">gatsby</span><span class="dl">'</span>

<span class="kd">const</span> <span class="nx">Layout</span> <span class="o">=</span> <span class="p">({</span> <span class="nx">pageTitle</span><span class="p">,</span> <span class="nx">children</span> <span class="p">})</span> <span class="o">=&gt;</span> <span class="p">{</span>
	<span class="k">return</span> <span class="p">(</span>
		<span class="o">&lt;</span><span class="nx">div</span><span class="o">&gt;</span>
			<span class="o">&lt;</span><span class="nx">Link</span> <span class="nx">to</span><span class="o">=</span><span class="dl">"</span><span class="s2">/</span><span class="dl">"</span><span class="o">&gt;</span><span class="nx">Home</span><span class="o">&lt;</span><span class="sr">/Link</span><span class="err">&gt;
</span>			<span class="o">&lt;</span><span class="nx">hr</span><span class="o">/&gt;</span>
			<span class="o">&lt;</span><span class="nx">h1</span><span class="o">&gt;</span><span class="p">{</span><span class="nx">pageTitle</span><span class="p">}</span><span class="o">&lt;</span><span class="sr">/h1</span><span class="err">&gt;
</span>			<span class="p">{</span><span class="nx">children</span><span class="p">}</span>
		<span class="o">&lt;</span><span class="sr">/div</span><span class="err">&gt;
</span>	<span class="p">)</span>
<span class="p">}</span>

<span class="k">export</span> <span class="k">default</span> <span class="nx">Layout</span>
</code></pre></div></div>

<p>And then referencing it in the about page like this:</p>

<div class="language-javascript highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="k">import</span> <span class="o">*</span> <span class="k">as</span> <span class="nx">React</span> <span class="k">from</span> <span class="dl">'</span><span class="s1">react</span><span class="dl">'</span>
<span class="k">import</span> <span class="nx">Layout</span> <span class="k">from</span> <span class="dl">'</span><span class="s1">../components/layout</span><span class="dl">'</span>

<span class="kd">const</span> <span class="nx">AboutPage</span> <span class="o">=</span> <span class="p">()</span> <span class="o">=&gt;</span> <span class="p">{</span>
	<span class="k">return</span> <span class="p">(</span>
		<span class="o">&lt;</span><span class="nx">Layout</span> <span class="nx">pageTitle</span><span class="o">=</span><span class="dl">"</span><span class="s2">About Me</span><span class="dl">"</span><span class="o">&gt;</span>
			<span class="o">&lt;</span><span class="nx">p</span><span class="o">&gt;</span><span class="nx">Hi</span> <span class="nx">there</span><span class="o">!</span> <span class="nx">I</span><span class="dl">'</span><span class="s1">m Richard.&lt;/p&gt;
		&lt;/Layout&gt;
	)
}
</span></code></pre></div></div>

<h3 id="deploying-using-github-actions">Deploying Using GitHub Actions</h3>

<p>Just like with Hugo, there are pre-written GitHub Actions for deploying a Gatsby site to GitHub Pages, and it works great.</p>

<p>In short, you can use GitHub Actions to run <code class="language-plaintext highlighter-rouge">npm run build</code> which generates the static content for your site in the <code class="language-plaintext highlighter-rouge">./public</code> directory, and then deploy that to GitHub Pages using the <code class="language-plaintext highlighter-rouge">peaceiris/actions-gh-pages@v4</code> GitHub Action.</p>

<p>Something like this will do the trick, in <code class="language-plaintext highlighter-rouge">.github/workflows/gatsby.yml</code>:</p>

<div class="language-yaml highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="na">name</span><span class="pi">:</span> <span class="s">Deploy Gatsby site to GitHub Pages</span>

<span class="na">on</span><span class="pi">:</span>
  <span class="na">push</span><span class="pi">:</span>
    <span class="na">branches</span><span class="pi">:</span> <span class="pi">[</span><span class="nv">main</span><span class="pi">]</span>

<span class="na">jobs</span><span class="pi">:</span>
  <span class="na">build-and-deploy</span><span class="pi">:</span>
    <span class="na">runs-on</span><span class="pi">:</span> <span class="s">ubuntu-latest</span>

    <span class="na">steps</span><span class="pi">:</span>
      <span class="pi">-</span> <span class="na">name</span><span class="pi">:</span> <span class="s">Checkout</span>
        <span class="na">uses</span><span class="pi">:</span> <span class="s">actions/checkout@v4</span>

      <span class="pi">-</span> <span class="na">name</span><span class="pi">:</span> <span class="s">Setup Node.js</span>
        <span class="na">uses</span><span class="pi">:</span> <span class="s">actions/setup-node@v4</span>
        <span class="na">with</span><span class="pi">:</span>
          <span class="na">node-version</span><span class="pi">:</span> <span class="s1">'</span><span class="s">20'</span>

      <span class="pi">-</span> <span class="na">name</span><span class="pi">:</span> <span class="s">Install dependencies</span>
        <span class="na">run</span><span class="pi">:</span> <span class="s">npm install</span>

      <span class="pi">-</span> <span class="na">name</span><span class="pi">:</span> <span class="s">Build Gatsby site</span>
        <span class="na">run</span><span class="pi">:</span> <span class="s">npm run build</span>

      <span class="pi">-</span> <span class="na">name</span><span class="pi">:</span> <span class="s">Deploy to GitHub Pages</span>
        <span class="na">uses</span><span class="pi">:</span> <span class="s">peaceiris/actions-gh-pages@v4</span>
        <span class="na">with</span><span class="pi">:</span>
          <span class="na">github_token</span><span class="pi">:</span> <span class="s">$</span>
          <span class="na">publish_dir</span><span class="pi">:</span> <span class="s">./public</span>

</code></pre></div></div>

<p>Now, the Gatsby documentation doesn’t seem to mention any hosting providers other than using Netlify, but as I say, you can host it anywhere you want.  Since Netlify’s acquisition of Gatsby in 2023, they seem to be strongly pushing Netlify as <em>the place</em> to host your Gatsby sites.</p>

<h3 id="a-note-on-disk-space">A Note On Disk Space</h3>

<p>Sometimes, the node ecosystem can lead to bloated disk space usage due to the number of packages downloaded to <code class="language-plaintext highlighter-rouge">node_modules</code>. Unfortunately, Gatsby is no exception to this, with my hello world example site weighing in at 629 MB, of which 567 MB is contained in the <code class="language-plaintext highlighter-rouge">node_modules</code> directory. Compare this with my hello world Hugo site from the previous post, which totals just 12 MB. For some with relatively modest storage capacity, this could be a real consideration when deciding which static site generator to use.</p>

<h3 id="summary">Summary</h3>

<p>Gatsby feels super powerful, but users without a decent grounding in React might find the learning curve to be pretty steep.  In terms of its use for hosting a simple blog, it’s likely overkill, particularly for the kind of thing I’d want to do.</p>]]></content><author><name></name></author><summary type="html"><![CDATA[Following on from the previous post about static site generators, the next one I took for a spin was Gatsby.]]></summary></entry><entry><title type="html">Static Site Generation in 2025 - Hugo</title><link href="http://www.richardfawcett.net/2025/06/12/static-site-generation-in-2025-hugo" rel="alternate" type="text/html" title="Static Site Generation in 2025 - Hugo" /><published>2025-06-12T00:00:00+00:00</published><updated>2025-06-12T00:00:00+00:00</updated><id>http://www.richardfawcett.net/2025/06/12/static-site-generation-in-2025-hugo</id><content type="html" xml:base="http://www.richardfawcett.net/2025/06/12/static-site-generation-in-2025-hugo"><![CDATA[<p>As I’m writing, this site is hosted on GitHub Pages, using Jekyll, which is supported out of the box.  I had a chat with my good friend ChatGPT, who suggests that Jekyll is not the only game in town.  It threw up Hugo and Gatsby as decent alternatives, with the following bulleted notes.</p>

<h3 id="hugo">Hugo</h3>
<ul>
  <li>Language: Go</li>
  <li>Speed: Extremely fast — one of the fastest SSGs.</li>
  <li>Templates: Go templating (a bit verbose but very flexible).</li>
  <li>Content Format: Markdown, TOML/YAML front matter.</li>
  <li>Strengths:
    <ul>
      <li>Very fast build times (even for large sites).</li>
      <li>Good multilingual support.</li>
      <li>Easy to install (single binary).</li>
    </ul>
  </li>
  <li>Weaknesses:
    <ul>
      <li>Go templating syntax is not intuitive for beginners.</li>
      <li>Less vibrant plugin ecosystem than Gatsby or Jekyll.</li>
    </ul>
  </li>
  <li>Best For: Large blogs, documentation sites, multilingual content.</li>
</ul>

<h3 id="gatsby">Gatsby</h3>
<ul>
  <li>Language: JavaScript/React</li>
  <li>Speed: Slower builds for large sites, though incremental builds improve this.</li>
  <li>Templates: React components.</li>
  <li>Content Format: Markdown, CMS, APIs, databases.</li>
  <li>Strengths:
    <ul>
      <li>Rich plugin ecosystem and GraphQL data layer.</li>
      <li>Integrates well with headless CMSs (Contentful, Sanity, etc.).</li>
      <li>Progressive web app features out of the box.</li>
    </ul>
  </li>
  <li>Weaknesses:
    <ul>
      <li>Steeper learning curve for non-React developers.</li>
      <li>Build performance issues with large sites.</li>
    </ul>
  </li>
  <li>Best For: Modern web apps, marketing sites, headless CMS integration.</li>
</ul>

<h3 id="jekyll">Jekyll</h3>
<ul>
  <li>Language: Ruby</li>
  <li>Speed: Slower than Hugo but fine for small to medium sites.</li>
  <li>Templates: Liquid templating.</li>
  <li>Content Format: Markdown, YAML front matter.</li>
  <li>Strengths:
    <ul>
      <li>Long-established, with a large user base.</li>
      <li>Officially supported by GitHub Pages.</li>
      <li>Simple and well-documented.</li>
    </ul>
  </li>
  <li>Weaknesses:
    <ul>
      <li>Ruby dependency can be troublesome on non-Ruby environments.</li>
      <li>Slow for large sites or complex plugins.</li>
    </ul>
  </li>
  <li>Best For: Blogs, personal websites, GitHub Pages users.</li>
</ul>

<p>While Jekyll and GitHub Pages have great integration, it should be quite straight-forward to deploy sites created with other static site generation tools to GitHub Pages using GitHub Actions.  I thought I’d take them for a spin, starting with Hugo.  In future posts, I’ll explore Gatsby, and also revisit Jekyll to see what the experience is like these days.</p>

<h2 id="hugo-1">Hugo</h2>

<p>Hugo, available at https://gohugo.io, bills itself as “the world’s fastest framework for building websites”.</p>

<p>Installation was honestly a little confusing.  First up, Hugo is available in three editions: standard, extended, and extended/deploy.  The <a href="https://gohugo.io/getting-started/quick-start/">quick start</a> says I need the extended or extended/deploy edition.  It seems a little odd that the quick start needs more than the standard edition.  Apparently, Homebrew (I’m running MacOS) installs the extended version of Hugo.  This makes me feel that the extended edition is really the standard edition.</p>

<div class="language-bash highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="nv">$ </span>brew <span class="nb">install </span>hugo
<span class="nv">$ </span>hugo version
hugo v0.147.8+extended+withdeploy darwin/arm64 <span class="nv">BuildDate</span><span class="o">=</span>2025-06-07T12:59:52Z <span class="nv">VendorInfo</span><span class="o">=</span>brew
</code></pre></div></div>

<p>Oh, ok, so it seems I got the extended/deploy version.  Whatever.  This is a bit of a mess. 🤔</p>

<p>Next up is the choice of Sass transpiler.  Extended is needed to transpile Sass to CSS using its embedded LibSass transpiler.  The Dart Sass transpiler can be used with any edition.  On the face of it, that sounds pretty straight-forward, i.e. Sass transpilation is included with the extended edition.  But then, in <a href="https://gohugo.io/functions/css/sass/#dart-sass">Hugo’s Sass documentation</a>, it says:</p>

<blockquote>
  <p>Hugo’s extended and extended/deploy editions include LibSass to transpile Sass to CSS. In 2020, the Sass team deprecated LibSass in favor of Dart Sass.</p>
</blockquote>

<p>Hmm. So I can install the extended edition to get deprecated Sass support out of the box.  Weird.  I installed Dart Sass on my system just to make sure I wasn’t going to be using anything deprecated (although I later found that the quick start doesn’t seem to require Sass, so this wasn’t a real issue).</p>

<div class="language-bash highlighter-rouge"><div class="highlight"><pre class="highlight"><code>brew <span class="nb">install </span>sass/sass/sass
</code></pre></div></div>

<p>At this point, I was pretty down on Hugo, I’ll be honest.  Things changed.  Once I got to <code class="language-plaintext highlighter-rouge">hugo new site quickstart</code> and beyond, things were great!</p>

<p>The availability of themes seems really strong, and they’re easily managed by installing with git’s submodule support, and the active theme is specified in the site’s TOML configuration file (while we’re here, +1 for using TOML as default, rather than just YAML).  Theming my site was as simple as:</p>

<div class="language-bash highlighter-rouge"><div class="highlight"><pre class="highlight"><code>git submodule add https://github.com/theNewDynamic/gohugo-theme-ananke.git themes/ananke
<span class="nb">echo</span> <span class="s2">"theme = 'ananke'"</span> <span class="o">&gt;&gt;</span> hugo.toml
</code></pre></div></div>

<p>Adding content was very straightforward too, with <code class="language-plaintext highlighter-rouge">hugo new content &lt;path_to_content&gt;</code>. The generated content file had straight-forward and easy to understand front matter.  New pages are marked as draft by default, and it’s trivial to included draft content when developing locally by running Hugo with <code class="language-plaintext highlighter-rouge">hugo server -D</code>.</p>

<p>Which brings us onto Hugo’s dev server.  It does everything you’d expect it to do … automatically picking up new content changes as soon as they’re saved to disk, and even causing the web page previewing your site to automatically refresh too.  And this reload is <em>blazingly</em> fast.</p>

<h3 id="deploying-using-github-actions">Deploying Using GitHub Actions</h3>

<p>While not as simple as GitHub’s build in support for Jekyll, it’s fairly straight-forward to use GitHub Actions to support deployment of a Hugo site.</p>

<p>I already have my main GitHub Pages repository, <code class="language-plaintext highlighter-rouge">yeurch.github.io</code> setup as https://richardfawcett.net, so any other repo I setup to use GitHub Pages will automatically get deployed as a subdirectory of that site (unless I over-ride it with a custom domain).  So I setup a repository <a href="https://github.com/yeurch/hugo-quickstart">hugo-quickstart</a> which, once configured for Pages, will deploy <a href="https://richardfawcett.net/hugo-quickstart">here</a>.</p>

<p>I pushed my changes to the repo, and went to the repository settigns and chose “Pages” in the left-sidebar.  I changed the rule from deploy from a branch to deploy using GitHub Actions, and I was presented with a list of possible actions.  Hugo wasn’t immediatley presented as a suggestion, but I searched through the available actions, and there <em>was</em> a suitable action for deployment.  GitHub guided me through committing this to my repo as file <code class="language-plaintext highlighter-rouge">.github/workflows/hugo.yml</code>, and it <em>just works</em>.  Here’s the content of the yml file:</p>

<div class="language-yml highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="c1"># Sample workflow for building and deploying a Hugo site to GitHub Pages</span>
<span class="na">name</span><span class="pi">:</span> <span class="s">Deploy Hugo site to Pages</span>

<span class="na">on</span><span class="pi">:</span>
  <span class="c1"># Runs on pushes targeting the default branch</span>
  <span class="na">push</span><span class="pi">:</span>
    <span class="na">branches</span><span class="pi">:</span> <span class="pi">[</span><span class="s2">"</span><span class="s">main"</span><span class="pi">]</span>

  <span class="c1"># Allows you to run this workflow manually from the Actions tab</span>
  <span class="na">workflow_dispatch</span><span class="pi">:</span>

<span class="c1"># Sets permissions of the GITHUB_TOKEN to allow deployment to GitHub Pages</span>
<span class="na">permissions</span><span class="pi">:</span>
  <span class="na">contents</span><span class="pi">:</span> <span class="s">read</span>
  <span class="na">pages</span><span class="pi">:</span> <span class="s">write</span>
  <span class="na">id-token</span><span class="pi">:</span> <span class="s">write</span>

<span class="c1"># Allow only one concurrent deployment, skipping runs queued between the run in-progress and latest queued.</span>
<span class="c1"># However, do NOT cancel in-progress runs as we want to allow these production deployments to complete.</span>
<span class="na">concurrency</span><span class="pi">:</span>
  <span class="na">group</span><span class="pi">:</span> <span class="s2">"</span><span class="s">pages"</span>
  <span class="na">cancel-in-progress</span><span class="pi">:</span> <span class="no">false</span>

<span class="c1"># Default to bash</span>
<span class="na">defaults</span><span class="pi">:</span>
  <span class="na">run</span><span class="pi">:</span>
    <span class="na">shell</span><span class="pi">:</span> <span class="s">bash</span>

<span class="na">jobs</span><span class="pi">:</span>
  <span class="c1"># Build job</span>
  <span class="na">build</span><span class="pi">:</span>
    <span class="na">runs-on</span><span class="pi">:</span> <span class="s">ubuntu-latest</span>
    <span class="na">env</span><span class="pi">:</span>
      <span class="na">HUGO_VERSION</span><span class="pi">:</span> <span class="s">0.128.0</span>
    <span class="na">steps</span><span class="pi">:</span>
      <span class="pi">-</span> <span class="na">name</span><span class="pi">:</span> <span class="s">Install Hugo CLI</span>
        <span class="na">run</span><span class="pi">:</span> <span class="pi">|</span>
          <span class="s">wget -O $/hugo.deb https://github.com/gohugoio/hugo/releases/download/v${HUGO_VERSION}/hugo_extended_${HUGO_VERSION}_linux-amd64.deb \</span>
          <span class="s">&amp;&amp; sudo dpkg -i $/hugo.deb</span>
      <span class="pi">-</span> <span class="na">name</span><span class="pi">:</span> <span class="s">Install Dart Sass</span>
        <span class="na">run</span><span class="pi">:</span> <span class="s">sudo snap install dart-sass</span>
      <span class="pi">-</span> <span class="na">name</span><span class="pi">:</span> <span class="s">Checkout</span>
        <span class="na">uses</span><span class="pi">:</span> <span class="s">actions/checkout@v4</span>
        <span class="na">with</span><span class="pi">:</span>
          <span class="na">submodules</span><span class="pi">:</span> <span class="s">recursive</span>
      <span class="pi">-</span> <span class="na">name</span><span class="pi">:</span> <span class="s">Setup Pages</span>
        <span class="na">id</span><span class="pi">:</span> <span class="s">pages</span>
        <span class="na">uses</span><span class="pi">:</span> <span class="s">actions/configure-pages@v5</span>
      <span class="pi">-</span> <span class="na">name</span><span class="pi">:</span> <span class="s">Install Node.js dependencies</span>
        <span class="na">run</span><span class="pi">:</span> <span class="s2">"</span><span class="s">[[</span><span class="nv"> </span><span class="s">-f</span><span class="nv"> </span><span class="s">package-lock.json</span><span class="nv"> </span><span class="s">||</span><span class="nv"> </span><span class="s">-f</span><span class="nv"> </span><span class="s">npm-shrinkwrap.json</span><span class="nv"> </span><span class="s">]]</span><span class="nv"> </span><span class="s">&amp;&amp;</span><span class="nv"> </span><span class="s">npm</span><span class="nv"> </span><span class="s">ci</span><span class="nv"> </span><span class="s">||</span><span class="nv"> </span><span class="s">true"</span>
      <span class="pi">-</span> <span class="na">name</span><span class="pi">:</span> <span class="s">Build with Hugo</span>
        <span class="na">env</span><span class="pi">:</span>
          <span class="na">HUGO_CACHEDIR</span><span class="pi">:</span> <span class="s">$/hugo_cache</span>
          <span class="na">HUGO_ENVIRONMENT</span><span class="pi">:</span> <span class="s">production</span>
        <span class="na">run</span><span class="pi">:</span> <span class="pi">|</span>
          <span class="s">hugo \</span>
            <span class="s">--minify \</span>
            <span class="s">--baseURL "$/"</span>
      <span class="pi">-</span> <span class="na">name</span><span class="pi">:</span> <span class="s">Upload artifact</span>
        <span class="na">uses</span><span class="pi">:</span> <span class="s">actions/upload-pages-artifact@v3</span>
        <span class="na">with</span><span class="pi">:</span>
          <span class="na">path</span><span class="pi">:</span> <span class="s">./public</span>

  <span class="c1"># Deployment job</span>
  <span class="na">deploy</span><span class="pi">:</span>
    <span class="na">environment</span><span class="pi">:</span>
      <span class="na">name</span><span class="pi">:</span> <span class="s">github-pages</span>
      <span class="na">url</span><span class="pi">:</span> <span class="s">$</span>
    <span class="na">runs-on</span><span class="pi">:</span> <span class="s">ubuntu-latest</span>
    <span class="na">needs</span><span class="pi">:</span> <span class="s">build</span>
    <span class="na">steps</span><span class="pi">:</span>
      <span class="pi">-</span> <span class="na">name</span><span class="pi">:</span> <span class="s">Deploy to GitHub Pages</span>
        <span class="na">id</span><span class="pi">:</span> <span class="s">deployment</span>
        <span class="na">uses</span><span class="pi">:</span> <span class="s">actions/deploy-pages@v4</span>

</code></pre></div></div>

<p>Committing this file automatically triggered itself, and it deployed my site as expected.  One issue … my post was still marked as draft, so I authored <a href="https://github.com/yeurch/hugo-quickstart/commit/957f8a2fe0aaf8c91c6a75585f9b70fe3a07f942">another commit</a> to properly publish it, pushed this up to GitHub, and it was reflected in my pages site within seconds.</p>]]></content><author><name></name></author><summary type="html"><![CDATA[As I’m writing, this site is hosted on GitHub Pages, using Jekyll, which is supported out of the box. I had a chat with my good friend ChatGPT, who suggests that Jekyll is not the only game in town. It threw up Hugo and Gatsby as decent alternatives, with the following bulleted notes.]]></summary></entry><entry><title type="html">Dotnet on Linux Feels First Class Now</title><link href="http://www.richardfawcett.net/2024/04/10/dotnet-on-linux-feels-first-class-now" rel="alternate" type="text/html" title="Dotnet on Linux Feels First Class Now" /><published>2024-04-10T00:00:00+00:00</published><updated>2024-04-10T00:00:00+00:00</updated><id>http://www.richardfawcett.net/2024/04/10/dotnet-on-linux-feels-first-class-now</id><content type="html" xml:base="http://www.richardfawcett.net/2024/04/10/dotnet-on-linux-feels-first-class-now"><![CDATA[<p>A long time ago, in another life it feels, I was a .NET developer, writing internal business
web applications on .NET Framework on Windows and IIS.  Support for .NET on Linux was just
through the mono project, and it always felt a little clunky, and lots of features were missing
compared to the “real” Microsoft .NET Framework.</p>

<p>Over the first few years of .NET Core, support for Linux was improved as more APIs were added
to the .NET Standard definition.  When the MAUI UI framework was released with .NET 6, I was
surprised and disappointed that it did not support Linux, only Windows, MacOS, Android and
iOS.  That said, there are some great third party frameworks for UI out there that do support
Linux, such as <a href="https://avaloniaui.net/">Avalonia</a>.</p>

<p>But when it comes to console apps and background services, I think it’s fair to say that recent .NET
Core versions treat Linux as a first class citizen.  I’ve recently been playing around with Microsoft’s
Redis-compatible server <a href="https://microsoft.github.io/garnet/docs">Garnet</a> on Windows, and wanted
to try it out on my Fedora Linux 39 machine, which doesn’t even have .NET on it.  I was expecting
a bunch of fairly complex installation steps, maybe adding in custom package repos, or manually
editing my <code class="language-plaintext highlighter-rouge">PATH</code> environment variable, that sort of thing.  It really wasn’t the case … Fedora
already has the .NET 8.0 SDK available as a package in the default software repos, so getting
setup with .NET was truly as simple as:</p>

<div class="language-bash highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="nb">sudo </span>dnf <span class="nb">install </span>dotnet-sdk-8.0
</code></pre></div></div>

<p>Amazing! And definitely easier than Windows.</p>

<p>Then, I followed the instructions for Garnet, and it <em>just worked, seamlessly</em>:</p>

<div class="language-bash highlighter-rouge"><div class="highlight"><pre class="highlight"><code>git clone https://github.com/microsoft/garnet
<span class="nb">cd </span>garnet
dotnet restore
dotnet build <span class="nt">-c</span> Release
<span class="nb">cd </span>main/GarnetServer
dotnet run <span class="nt">-c</span> Release <span class="nt">-f</span> net8.0
</code></pre></div></div>

<p>And that was it … Garnet was up and running, with identical build and execution steps as on
Windows (save for the forward slash vs backslash when changing directory).</p>

<p><img src="/assets/images/20240410_dotnet/garnet.png" alt="Garnet running on Fedora 39" /></p>]]></content><author><name></name></author><summary type="html"><![CDATA[A long time ago, in another life it feels, I was a .NET developer, writing internal business web applications on .NET Framework on Windows and IIS. Support for .NET on Linux was just through the mono project, and it always felt a little clunky, and lots of features were missing compared to the “real” Microsoft .NET Framework.]]></summary></entry><entry><title type="html">Monty Hall and Generative AI</title><link href="http://www.richardfawcett.net/2024/02/01/monty-hall-and-generative-ai" rel="alternate" type="text/html" title="Monty Hall and Generative AI" /><published>2024-02-01T00:00:00+00:00</published><updated>2024-02-01T00:00:00+00:00</updated><id>http://www.richardfawcett.net/2024/02/01/monty-hall-and-generative-ai</id><content type="html" xml:base="http://www.richardfawcett.net/2024/02/01/monty-hall-and-generative-ai"><![CDATA[<p>It’s a fair bet that you’ve heard of the <a href="https://en.wikipedia.org/wiki/Monty_Hall_problem">Monty Hall problem</a>.  It became famous as a question from reader Craig F. Whitaker’s letter quoted in Marilyn vos Savant’s “Ask Marilyn” column in Parade magazine in 1990:</p>

<blockquote>
  <p>Suppose you’re on a game show, and you’re given the choice of three doors: Behind one door is a car; behind the others, goats. You pick a door, say No. 1, and the host, who knows what’s behind the doors, opens another door, say No. 3, which has a goat. He then says to you, “Do you want to pick door No. 2?” Is it to your advantage to switch your choice?</p>
</blockquote>

<p>In short, if you don’t switch your choice, your chances of winning are 1/3, whereas perhaps a little counter-intuitively, if you <em>do</em> switch, your chances increase to 2/3. This isn’t a post about why that’s the case … please do go ahead and do your own thought experiment, or read about it some more in the Wikipedia article linked above.</p>

<p>No, what this post is about is programming. My 15-year-old daughter is doing an GCSE in Computing, and learning Python.  The Monty Hall problem came up in conversation a few days ago so I suggested to her that we programmed a simulation of the problem in Python.  We spent maybe a half-hour writing some Python that played the game 10,000 times switching, and 10,000 times not switching.  It was really satisfying for my daughter to see the numbers come out pretty close to what was expected … around 3,300 wins if the program doesn’t switch boxes, and around 6,700 wins if it switches.  Who am I even kidding, it was satisfying for me too.</p>

<p>Now, I’ve been playing around in my free time with the generative AI <a href="https://chat.openai.com">ChatGPT</a> for the last week or so.  I don’t have a subscription, so I’m limited to the older “inferior” ChatGPT 3.5 on my free account.  I’ve heard it can generate source code in various languages, but until tonight, I’d never put it to the test.  This seemed like a great opportunity to give it a try.  Here’s what I asked ChatGPT:</p>

<blockquote>
  <p>Write a Python program to calculate the probability of winning the Monty Hall box problem if you swap boxes, and also if you don’t swap.</p>
</blockquote>

<p>Quick as a flash, the AI responded with a brief outline of the problem, and a Python program to simulate it.  What took my daughter and me half an hour would have likely taken me five to ten minutes on my own.  ChatGPT gave a correct answer almost instantaneously; I ran the program it generated and it gave the expected statistics immediately.  I’ll leave you now with the wise words of ChatGPT:</p>

<p>Here’s a link to the transcript of our conversation: <a href="https://chat.openai.com/share/b19cce6f-11e6-4889-9d01-f40ccf83af61">https://chat.openai.com/share/b19cce6f-11e6-4889-9d01-f40ccf83af61</a>.</p>

<h2 id="chatgpt-response">ChatGPT Response</h2>

<blockquote>
  <p>Certainly! The Monty Hall problem is a famous probability puzzle. In the problem, a contestant is presented with three doors. Behind one door is a car (the prize), and behind the other two doors are goats. The contestant initially picks one door, and then the host (Monty Hall) opens one of the other two doors, revealing a goat. The contestant is then given the option to either stick with their original choice or switch to the other unopened door. The question is: what is the probability of winning if you stick with your initial choice versus if you switch?</p>
</blockquote>

<blockquote>
  <p>Here’s a Python program to simulate and calculate the probabilities for both strategies:</p>
</blockquote>

<div class="language-python highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="kn">import</span> <span class="nn">random</span>

<span class="k">def</span> <span class="nf">monty_hall_simulation</span><span class="p">(</span><span class="n">num_trials</span><span class="p">):</span>
    <span class="n">stick_wins</span> <span class="o">=</span> <span class="mi">0</span>
    <span class="n">switch_wins</span> <span class="o">=</span> <span class="mi">0</span>

    <span class="k">for</span> <span class="n">_</span> <span class="ow">in</span> <span class="nb">range</span><span class="p">(</span><span class="n">num_trials</span><span class="p">):</span>
        <span class="c1"># Initialize doors with one car and two goats
</span>        <span class="n">doors</span> <span class="o">=</span> <span class="p">[</span><span class="s">'goat'</span><span class="p">,</span> <span class="s">'goat'</span><span class="p">,</span> <span class="s">'car'</span><span class="p">]</span>
        
        <span class="c1"># Contestant makes an initial choice
</span>        <span class="n">contestant_choice</span> <span class="o">=</span> <span class="n">random</span><span class="p">.</span><span class="n">randint</span><span class="p">(</span><span class="mi">0</span><span class="p">,</span> <span class="mi">2</span><span class="p">)</span>
        
        <span class="c1"># Monty Hall opens a door with a goat
</span>        <span class="n">remaining_doors</span> <span class="o">=</span> <span class="p">[</span><span class="n">i</span> <span class="k">for</span> <span class="n">i</span> <span class="ow">in</span> <span class="nb">range</span><span class="p">(</span><span class="mi">3</span><span class="p">)</span> <span class="k">if</span> <span class="n">i</span> <span class="o">!=</span> <span class="n">contestant_choice</span> <span class="ow">and</span> <span class="n">doors</span><span class="p">[</span><span class="n">i</span><span class="p">]</span> <span class="o">==</span> <span class="s">'goat'</span><span class="p">]</span>
        <span class="n">monty_opens</span> <span class="o">=</span> <span class="n">random</span><span class="p">.</span><span class="n">choice</span><span class="p">(</span><span class="n">remaining_doors</span><span class="p">)</span>
        
        <span class="c1"># Contestant chooses to either stick or switch
</span>        <span class="n">stick_choice</span> <span class="o">=</span> <span class="n">contestant_choice</span>
        <span class="n">switch_choice</span> <span class="o">=</span> <span class="p">[</span><span class="n">i</span> <span class="k">for</span> <span class="n">i</span> <span class="ow">in</span> <span class="nb">range</span><span class="p">(</span><span class="mi">3</span><span class="p">)</span> <span class="k">if</span> <span class="n">i</span> <span class="o">!=</span> <span class="n">contestant_choice</span> <span class="ow">and</span> <span class="n">i</span> <span class="o">!=</span> <span class="n">monty_opens</span><span class="p">][</span><span class="mi">0</span><span class="p">]</span>
        
        <span class="c1"># Check if the contestant wins by sticking
</span>        <span class="k">if</span> <span class="n">doors</span><span class="p">[</span><span class="n">stick_choice</span><span class="p">]</span> <span class="o">==</span> <span class="s">'car'</span><span class="p">:</span>
            <span class="n">stick_wins</span> <span class="o">+=</span> <span class="mi">1</span>
        
        <span class="c1"># Check if the contestant wins by switching
</span>        <span class="k">if</span> <span class="n">doors</span><span class="p">[</span><span class="n">switch_choice</span><span class="p">]</span> <span class="o">==</span> <span class="s">'car'</span><span class="p">:</span>
            <span class="n">switch_wins</span> <span class="o">+=</span> <span class="mi">1</span>
    
    <span class="c1"># Calculate probabilities
</span>    <span class="n">stick_probability</span> <span class="o">=</span> <span class="n">stick_wins</span> <span class="o">/</span> <span class="n">num_trials</span>
    <span class="n">switch_probability</span> <span class="o">=</span> <span class="n">switch_wins</span> <span class="o">/</span> <span class="n">num_trials</span>
    
    <span class="k">return</span> <span class="n">stick_probability</span><span class="p">,</span> <span class="n">switch_probability</span>

<span class="k">if</span> <span class="n">__name__</span> <span class="o">==</span> <span class="s">"__main__"</span><span class="p">:</span>
    <span class="n">num_trials</span> <span class="o">=</span> <span class="mi">100000</span>  <span class="c1"># You can adjust the number of trials
</span>    <span class="n">stick_prob</span><span class="p">,</span> <span class="n">switch_prob</span> <span class="o">=</span> <span class="n">monty_hall_simulation</span><span class="p">(</span><span class="n">num_trials</span><span class="p">)</span>
    
    <span class="k">print</span><span class="p">(</span><span class="sa">f</span><span class="s">"Probability of winning by sticking: </span><span class="si">{</span><span class="n">stick_prob</span><span class="si">:</span><span class="p">.</span><span class="mi">4</span><span class="n">f</span><span class="si">}</span><span class="s">"</span><span class="p">)</span>
    <span class="k">print</span><span class="p">(</span><span class="sa">f</span><span class="s">"Probability of winning by switching: </span><span class="si">{</span><span class="n">switch_prob</span><span class="si">:</span><span class="p">.</span><span class="mi">4</span><span class="n">f</span><span class="si">}</span><span class="s">"</span><span class="p">)</span>
</code></pre></div></div>

<blockquote>
  <p>This program simulates the Monty Hall problem for a specified number of trials and calculates the probabilities of winning by sticking or switching. Keep in mind that the actual probabilities are approximately 1/3 for sticking and 2/3 for switching, demonstrating the advantage of the switching strategy.</p>
</blockquote>]]></content><author><name></name></author><summary type="html"><![CDATA[It’s a fair bet that you’ve heard of the Monty Hall problem. It became famous as a question from reader Craig F. Whitaker’s letter quoted in Marilyn vos Savant’s “Ask Marilyn” column in Parade magazine in 1990:]]></summary></entry><entry><title type="html">Compiling Amiga Specific C (Part 5 of 5)</title><link href="http://www.richardfawcett.net/2021/05/28/compiling-amiga-specific-c-part-5-of-5" rel="alternate" type="text/html" title="Compiling Amiga Specific C (Part 5 of 5)" /><published>2021-05-28T00:00:00+00:00</published><updated>2021-05-28T00:00:00+00:00</updated><id>http://www.richardfawcett.net/2021/05/28/compiling-amiga-specific-c-part-5-of-5</id><content type="html" xml:base="http://www.richardfawcett.net/2021/05/28/compiling-amiga-specific-c-part-5-of-5"><![CDATA[<p>This is the final part of a five-part series of posts on compiling Amiga-specific C code on an (emulated) Amiga A1200. We’ll start with a machine with an empty hard drive, and over the series, build up to compiling an Amiga-specific program which uses the graphics library.</p>

<ul>
  <li>Part 1 – <a href="/2021/04/30/compiling-amiga-specific-c-part-1-of-5.html">Introduction and motivation</a></li>
  <li>Part 2 – <a href="/2021/05/08/compiling-amiga-specific-c-part-2-of-5.html">Installing Workbench and necessary utilities</a></li>
  <li>Part 3 – <a href="/2021/05/12/compiling-amiga-specific-c-part-3-of-5.html">Installing VBCC and the Amiga include files</a></li>
  <li>Part 4 – <a href="/2021/05/19/compiling-amiga-specific-c-part-4-of-5.html">Compiling a simple Hello World program</a></li>
  <li><strong>Part 5 – Compiling some Amiga-specific code</strong> (this part)</li>
</ul>

<p>Let’s go!</p>

<h2 id="compiling-some-amiga-sepcific-code">Compiling some Amiga-sepcific code</h2>

<p>It’s time for some proper Amiga coding! We’ll take some example code from the Amiga Intuition Reference Manual, published by Commodore in 1986, we’ll get it to compile with VBCC. We’ll finish by talking about different compiler options that you may need if you want to go on and make modifications to the program, or write your own killer application.</p>

<p><strong>The example code</strong></p>

<p>Here it is. It’s about as straight-forward as it can be. The program creates a screen, opens up a window on that screen, and prints a “Hello Amiga” message in the new Window. It then listens for the user pressing the close button, and shuts down everything cleanly.</p>

<p>I had to make a few modifications from the original as-printed version … it declared some unused variables, and didn’t cleanly close its libraries. Anyway, here it is …</p>

<figure class="highlight"><pre><code class="language-c" data-lang="c"><span class="cm">/*****************************************
 * 
 * "Hello Amiga"
 * 
 *****************************************/</span>

<span class="cp">#include</span> <span class="cpf">&lt;exec/types.h&gt;</span><span class="cp">
#include</span> <span class="cpf">&lt;intuition/intuition.h&gt;</span><span class="cp">
</span>
<span class="k">struct</span> <span class="n">IntuitionBase</span> <span class="o">*</span><span class="n">IntuitionBase</span><span class="p">;</span>
<span class="k">struct</span> <span class="n">GfxBase</span>       <span class="o">*</span><span class="n">GfxBase</span><span class="p">;</span>

<span class="cp">#define INTUITION_REV 0
#define GRAPHICS_REV  0
</span>
<span class="k">struct</span> <span class="n">TextAttr</span> <span class="n">MyFont</span> <span class="o">=</span> <span class="p">{</span>
    <span class="s">"topaz.font"</span><span class="p">,</span>   <span class="cm">/* Font name */</span>
    <span class="n">TOPAZ_SIXTY</span><span class="p">,</span>    <span class="cm">/* Font height */</span>
    <span class="n">FS_NORMAL</span><span class="p">,</span>      <span class="cm">/* Font style */</span>
    <span class="n">FPF_ROMFONT</span><span class="p">,</span>    <span class="cm">/* Preferences */</span>
<span class="p">};</span>

<span class="k">struct</span> <span class="n">NewScreen</span> <span class="n">NewScreen</span> <span class="o">=</span> <span class="p">{</span>
    <span class="mi">0</span><span class="p">,</span>               <span class="cm">/* left */</span>
    <span class="mi">0</span><span class="p">,</span>               <span class="cm">/* top */</span>
    <span class="mi">320</span><span class="p">,</span>             <span class="cm">/* width */</span>
    <span class="mi">200</span><span class="p">,</span>             <span class="cm">/* height */</span>
    <span class="mi">2</span><span class="p">,</span>               <span class="cm">/* bit depth (4 colors) */</span>
    <span class="mi">0</span><span class="p">,</span> <span class="mi">1</span><span class="p">,</span>            <span class="cm">/* detail pen and block pen */</span>
    <span class="nb">NULL</span><span class="p">,</span>            <span class="cm">/* no special display modes */</span>
    <span class="n">CUSTOMSCREEN</span><span class="p">,</span>    <span class="cm">/* screen type */</span>
    <span class="o">&amp;</span><span class="n">MyFont</span><span class="p">,</span>         <span class="cm">/* use the font we defined above */</span>
    <span class="s">"My Own Screen"</span><span class="p">,</span> <span class="cm">/* screen title */</span>
    <span class="nb">NULL</span><span class="p">,</span>            <span class="cm">/* no special screen gadgets */</span>
    <span class="nb">NULL</span><span class="p">,</span>            <span class="cm">/* no special CustomBitMap */</span>
<span class="p">};</span>

<span class="n">main</span><span class="p">()</span> <span class="p">{</span>
    <span class="k">struct</span> <span class="n">Screen</span> <span class="o">*</span><span class="n">Screen</span><span class="p">;</span>
    <span class="k">struct</span> <span class="n">NewWindow</span> <span class="n">NewWindow</span><span class="p">;</span>
    <span class="k">struct</span> <span class="n">Window</span> <span class="o">*</span><span class="n">Window</span><span class="p">;</span>

    <span class="cm">/* Open intuition library */</span>
    <span class="n">IntuitionBase</span> <span class="o">=</span> <span class="p">(</span><span class="k">struct</span> <span class="n">IntuitionBase</span><span class="o">*</span><span class="p">)</span><span class="n">OpenLibrary</span><span class="p">(</span><span class="s">"intuition.library"</span><span class="p">,</span> <span class="n">INTUITION_REV</span><span class="p">);</span>
    <span class="k">if</span> <span class="p">(</span><span class="n">IntuitionBase</span> <span class="o">==</span> <span class="nb">NULL</span><span class="p">)</span> <span class="n">exit</span><span class="p">(</span><span class="n">FALSE</span><span class="p">);</span>

    <span class="n">GfxBase</span> <span class="o">=</span> <span class="p">(</span><span class="k">struct</span> <span class="n">GfxBase</span><span class="o">*</span><span class="p">)</span><span class="n">OpenLibrary</span><span class="p">(</span><span class="s">"graphics.library"</span><span class="p">,</span> <span class="n">GRAPHICS_REV</span><span class="p">);</span>
    <span class="k">if</span> <span class="p">(</span><span class="n">GfxBase</span> <span class="o">==</span> <span class="nb">NULL</span><span class="p">)</span> <span class="n">exit</span><span class="p">(</span><span class="n">FALSE</span><span class="p">);</span>

    <span class="k">if</span> <span class="p">((</span><span class="n">Screen</span> <span class="o">=</span> <span class="p">(</span><span class="k">struct</span> <span class="n">Screen</span><span class="o">*</span><span class="p">)</span><span class="n">OpenScreen</span><span class="p">(</span><span class="o">&amp;</span><span class="n">NewScreen</span><span class="p">))</span> <span class="o">==</span> <span class="nb">NULL</span><span class="p">)</span> <span class="n">exit</span><span class="p">(</span><span class="n">FALSE</span><span class="p">);</span>

    <span class="n">NewWindow</span><span class="p">.</span><span class="n">LeftEdge</span> <span class="o">=</span> <span class="mi">20</span><span class="p">;</span>
    <span class="n">NewWindow</span><span class="p">.</span><span class="n">TopEdge</span> <span class="o">=</span> <span class="mi">20</span><span class="p">;</span>
    <span class="n">NewWindow</span><span class="p">.</span><span class="n">Width</span> <span class="o">=</span> <span class="mi">300</span><span class="p">;</span>
    <span class="n">NewWindow</span><span class="p">.</span><span class="n">Height</span> <span class="o">=</span> <span class="mi">100</span><span class="p">;</span>
    <span class="n">NewWindow</span><span class="p">.</span><span class="n">DetailPen</span> <span class="o">=</span> <span class="mi">0</span><span class="p">;</span>
    <span class="n">NewWindow</span><span class="p">.</span><span class="n">BlockPen</span> <span class="o">=</span> <span class="mi">1</span><span class="p">;</span>
    <span class="n">NewWindow</span><span class="p">.</span><span class="n">Title</span> <span class="o">=</span> <span class="s">"A Simple Window"</span><span class="p">;</span>
    <span class="n">NewWindow</span><span class="p">.</span><span class="n">Flags</span> <span class="o">=</span> <span class="n">WINDOWCLOSE</span> <span class="o">|</span> <span class="n">SMART_REFRESH</span> <span class="o">|</span> <span class="n">ACTIVATE</span> <span class="o">|</span> <span class="n">WINDOWSIZING</span> <span class="o">|</span>
            <span class="n">WINDOWDRAG</span> <span class="o">|</span> <span class="n">WINDOWDEPTH</span> <span class="o">|</span> <span class="n">NOCAREREFRESH</span><span class="p">;</span>
    <span class="n">NewWindow</span><span class="p">.</span><span class="n">IDCMPFlags</span> <span class="o">=</span> <span class="n">CLOSEWINDOW</span><span class="p">;</span>
    <span class="n">NewWindow</span><span class="p">.</span><span class="n">Type</span> <span class="o">=</span> <span class="n">CUSTOMSCREEN</span><span class="p">;</span>
    <span class="n">NewWindow</span><span class="p">.</span><span class="n">FirstGadget</span> <span class="o">=</span> <span class="nb">NULL</span><span class="p">;</span>
    <span class="n">NewWindow</span><span class="p">.</span><span class="n">CheckMark</span> <span class="o">=</span> <span class="nb">NULL</span><span class="p">;</span>
    <span class="n">NewWindow</span><span class="p">.</span><span class="n">Screen</span> <span class="o">=</span> <span class="n">Screen</span><span class="p">;</span>
    <span class="n">NewWindow</span><span class="p">.</span><span class="n">BitMap</span> <span class="o">=</span> <span class="nb">NULL</span><span class="p">;</span>
    <span class="n">NewWindow</span><span class="p">.</span><span class="n">MinWidth</span> <span class="o">=</span> <span class="mi">100</span><span class="p">;</span>
    <span class="n">NewWindow</span><span class="p">.</span><span class="n">MinHeight</span> <span class="o">=</span> <span class="mi">25</span><span class="p">;</span>
    <span class="n">NewWindow</span><span class="p">.</span><span class="n">MaxWidth</span> <span class="o">=</span> <span class="mi">640</span><span class="p">;</span>
    <span class="n">NewWindow</span><span class="p">.</span><span class="n">MaxHeight</span> <span class="o">=</span> <span class="mi">200</span><span class="p">;</span>

    <span class="k">if</span> <span class="p">((</span> <span class="n">Window</span> <span class="o">=</span> <span class="p">(</span><span class="k">struct</span> <span class="n">Window</span><span class="o">*</span><span class="p">)</span><span class="n">OpenWindow</span><span class="p">(</span><span class="o">&amp;</span><span class="n">NewWindow</span><span class="p">))</span> <span class="o">==</span> <span class="nb">NULL</span><span class="p">)</span> <span class="n">exit</span><span class="p">(</span><span class="n">FALSE</span><span class="p">);</span>

    <span class="n">Move</span><span class="p">(</span><span class="n">Window</span><span class="o">-&gt;</span><span class="n">RPort</span><span class="p">,</span> <span class="mi">20</span><span class="p">,</span> <span class="mi">20</span><span class="p">);</span>
    <span class="n">Text</span><span class="p">(</span><span class="n">Window</span><span class="o">-&gt;</span><span class="n">RPort</span><span class="p">,</span> <span class="s">"Hello Amiga"</span><span class="p">,</span> <span class="mi">11</span><span class="p">);</span>

    <span class="n">Wait</span><span class="p">(</span><span class="mi">1</span> <span class="o">&lt;&lt;</span> <span class="n">Window</span><span class="o">-&gt;</span><span class="n">UserPort</span><span class="o">-&gt;</span><span class="n">mp_SigBit</span><span class="p">);</span>

    <span class="n">CloseWindow</span><span class="p">(</span><span class="n">Window</span><span class="p">);</span>
    <span class="n">CloseScreen</span><span class="p">(</span><span class="n">Screen</span><span class="p">);</span>
    <span class="n">CloseLibrary</span><span class="p">(</span><span class="n">GfxBase</span><span class="p">);</span>
    <span class="n">CloseLibrary</span><span class="p">(</span><span class="n">IntuitionBase</span><span class="p">);</span>

    <span class="n">exit</span><span class="p">(</span><span class="n">TRUE</span><span class="p">);</span>
<span class="p">}</span></code></pre></figure>

<p>You <em>could</em> type this into <code class="language-plaintext highlighter-rouge">memacs</code> or <code class="language-plaintext highlighter-rouge">edit</code>, but that’s going to be really hard work. I’d recommend using a text editor on your host system, and saving the file into the Shared folder that we’ve been using to share files between the host and our Amiga VM. I saved it as <code class="language-plaintext highlighter-rouge">hello_amiga.c</code>.</p>

<p><strong>Compiling the program</strong></p>

<p>The naive approach would be to use something similar to the command we used last time to compile the hello world program:</p>

<div class="language-plaintext highlighter-rouge"><div class="highlight"><pre class="highlight"><code>vc -o hello_amiga hello_amiga.c
</code></pre></div></div>

<p>That’s going to produce a bunch of linker errors. Our file compiles just fine, but it makes use of externally declared symbols, like <code class="language-plaintext highlighter-rouge">OpenScreen</code>. Well, actually it’s <code class="language-plaintext highlighter-rouge">_OpenScreen</code> … I guess the header files have inline code which adds an underscore and calls an externally defined function of that name. Then the linker comes to tie everything together and produce our executable, it can’t resolve the definition of that routine, so we get linker errors.</p>

<p>vbcc ships with a <code class="language-plaintext highlighter-rouge">amiga.lib</code> file which contains the implementations of all of the Amiga-specific functions. When we’re compiling a program, we have to tell <code class="language-plaintext highlighter-rouge">vc</code> that we want to link with that library by adding <code class="language-plaintext highlighter-rouge">-lamiga</code> to our command line.</p>

<div class="language-plaintext highlighter-rouge"><div class="highlight"><pre class="highlight"><code>vc -lamiga -o hello_amiga hello_amiga.c
</code></pre></div></div>

<p>This compiles, and running our executable with <code class="language-plaintext highlighter-rouge">hello_amiga</code> works as expected …</p>

<p><img src="/assets/images/20210430_amiga/part5.png" alt="Image 1" /></p>

<p>OK, I didn’t say it was <em>pretty</em>.</p>

<h2 id="some-other-considerations">Some other considerations</h2>

<p><strong>Automatic library opening</strong></p>

<p>We can remove some of the boiler plate that opens and closes the libraries we need (“intuition.library” and “graphics.library”), as vbcc will handle automatically opening and closing them for us.</p>

<p>Remove the definitions of <code class="language-plaintext highlighter-rouge">IntuitionBase</code> and <code class="language-plaintext highlighter-rouge">GfxBase</code> (lines 10 and 11) and all the lines that refer to them.</p>

<p>With the same invocation of <code class="language-plaintext highlighter-rouge">vc</code> as above, we get a load more linker errors, complaining that <code class="language-plaintext highlighter-rouge">_GfxBase</code> and <code class="language-plaintext highlighter-rouge">_IntuitionBase</code> are not defined. We need to link with another magic library that ships with <code class="language-plaintext highlighter-rouge">vbcc</code>, this one is <code class="language-plaintext highlighter-rouge">auto.lib</code>. To link against it, we add <code class="language-plaintext highlighter-rouge">-lauto</code> to the command line …</p>

<div class="language-plaintext highlighter-rouge"><div class="highlight"><pre class="highlight"><code>vc -lamiga -lauto -o hello_amiga hello_amiga.c
</code></pre></div></div>

<p><strong>Floating point math</strong></p>

<p>At some point, you’re going to want to use some floating point math. E.g. double types and all that good stuff. Well, it doesn’t “just work”. You have to link with yet another vbcc provided library, this time you need to specify:</p>

<div class="language-plaintext highlighter-rouge"><div class="highlight"><pre class="highlight"><code>-lmieee.
</code></pre></div></div>

<p>Alternatively, if you don’t need 68000 support, and you can target just 68020 processors or better (i.e. Amiga 1200 or later), you can instead make use of the FPU (floating point unit) with these commands:</p>

<div class="language-plaintext highlighter-rouge"><div class="highlight"><pre class="highlight"><code>-lm881 -fpu=68881
</code></pre></div></div>

<p><strong>C-99 support</strong></p>

<p>It turns out that vbcc by default supports only a really old version of C. For example, you can’t even use end of line comments prefixed with <code class="language-plaintext highlighter-rouge">//</code>. You can get C-99 support by adding the following switch to the command-line:</p>

<div class="language-plaintext highlighter-rouge"><div class="highlight"><pre class="highlight"><code>-c99
</code></pre></div></div>

<h2 id="in-conclusion">In conclusion</h2>

<p>So, that’s about it. We’ve gone from a pristeen Amiga, installed everything we need to do C development, and got some Amiga-specific code to compile and run.</p>

<p>Where you go from here is up to you. I’d highly recommend getting some of the official Amiga reference manuals. There are copies available online, but I’m not sure how legitimate that is from a copyright perspective. Some that you could check out are the “Amiga Intuition Reference Manual”, the “Amiga Hardware Reference Manual”, “Amiga ROM Kernel: Devices” and the “Amiga ROM Kernel Reference Manual: Libraries and Devices”.</p>

<p>Good luck!</p>]]></content><author><name></name></author><summary type="html"><![CDATA[This is the final part of a five-part series of posts on compiling Amiga-specific C code on an (emulated) Amiga A1200. We’ll start with a machine with an empty hard drive, and over the series, build up to compiling an Amiga-specific program which uses the graphics library.]]></summary></entry><entry><title type="html">Compiling Amiga Specific C (Part 4 of 5)</title><link href="http://www.richardfawcett.net/2021/05/19/compiling-amiga-specific-c-part-4-of-5" rel="alternate" type="text/html" title="Compiling Amiga Specific C (Part 4 of 5)" /><published>2021-05-19T00:00:00+00:00</published><updated>2021-05-19T00:00:00+00:00</updated><id>http://www.richardfawcett.net/2021/05/19/compiling-amiga-specific-c-part-4-of-5</id><content type="html" xml:base="http://www.richardfawcett.net/2021/05/19/compiling-amiga-specific-c-part-4-of-5"><![CDATA[<p>This is part four of a five-part series of posts on compiling Amiga-specific C code on an (emulated) Amiga A1200. We’ll start with a machine with an empty hard drive, and over the series, build up to compiling an Amiga-specific program which uses the graphics library.</p>

<ul>
  <li>Part 1 – <a href="/2021/04/30/compiling-amiga-specific-c-part-1-of-5.html">Introduction and motivation</a></li>
  <li>Part 2 – <a href="/2021/05/08/compiling-amiga-specific-c-part-2-of-5.html">Installing Workbench and necessary utilities</a></li>
  <li>Part 3 – <a href="/2021/05/12/compiling-amiga-specific-c-part-3-of-5.html">Installing VBCC and the Amiga include files</a></li>
  <li><strong>Part 4 – Compiling a simple Hello World program</strong> (this part)</li>
  <li>Part 5 – <a href="/2021/05/28/compiling-amiga-specific-c-part-5-of-5.html">Compiling some Amiga-specific code</a></li>
</ul>

<p>Let’s go!</p>

<h2 id="compiling-a-simple-hello-world-program">Compiling a simple Hello World program</h2>

<p>This one should be a pretty simple one. We’re going to compile a simple Hello World program to basically ensure that we installed our compiler and include files correctly.</p>

<p>So, here’s the simple C program we’re going to be compiling. It’s very straight-forward:</p>

<figure class="highlight"><pre><code class="language-c" data-lang="c"><span class="cp">#include</span> <span class="cpf">&lt;stdio.h&gt;</span><span class="cp">
</span>
<span class="kt">int</span> <span class="nf">main</span><span class="p">(</span><span class="kt">int</span> <span class="n">argc</span><span class="p">,</span> <span class="kt">char</span><span class="o">**</span> <span class="n">argv</span><span class="p">)</span> <span class="p">{</span>
    <span class="n">printf</span><span class="p">(</span><span class="s">"Hello World, from the Amiga.</span><span class="se">\n</span><span class="s">"</span><span class="p">);</span>
    <span class="k">return</span> <span class="mi">0</span><span class="p">;</span>
<span class="p">}</span></code></pre></figure>

<p>AmigaOS comes complete with a text editor called memacs. While primitive by today’s standards, it was a great program for its time. Let’s fire it up by opening a Shell, changing to the <code class="language-plaintext highlighter-rouge">RAM:</code> directory, and editing a new file we’ll name <code class="language-plaintext highlighter-rouge">hello.c</code>.</p>

<p><img src="/assets/images/20210430_amiga/part4-01.png" alt="Image 1" /></p>

<p>Type in our program, before choosing “Save-Exit” from the Project menu.</p>

<p><img src="/assets/images/20210430_amiga/part4-02.png" alt="Image 2" /></p>

<p>The C compiler that comes as part of VBCC is actually called vc, and we want to specify an output file of hello by using the -o parameter.</p>

<figure class="highlight"><pre><code class="language-plaintext" data-lang="plaintext">3.Ram Disk:&gt; vc hello.c -o hello</code></pre></figure>

<p>Assuming we didn’t get any errors, we can then run the program by executing <code class="language-plaintext highlighter-rouge">hello</code>, like this:</p>

<p><img src="/assets/images/20210430_amiga/part4-03.png" alt="Image 3" /></p>

<p>That’s really all there is to it. If you got errors when compiling, it’s worth checking <code class="language-plaintext highlighter-rouge">S:user-startup</code> to ensure that it contains the VBCC stuff below (the installer should have automatically added this).</p>

<figure class="highlight"><pre><code class="language-plaintext" data-lang="plaintext">;BEGIN vbcc
assign &gt;NIL: vbcc: System:c_dev/vbcc
assign &gt;NIL: C: vbcc:bin ADD
setenv VBCC vbcc:
;END vbcc
;BEGIN vbcc-m68k-amigaos
assign &gt;NIL: vincludeos3: vbcc:targets/m68k-amigaos/include
assign &gt;NIL: vincludeos3: "System:c_dev/NDK_3.9/Include/include_h" add
assign &gt;NIL: vlibos3: vbcc:targets/m68k-amigaos/lib
;END vbcc-m68k-amigaos</code></pre></figure>

<p>So, to summarize, we’ve verified that the compiler is installed and working correctly, and can locate and use the standard include files. Next time, we’ll move on to our actual goal of compiling some Amiga-specific code!</p>]]></content><author><name></name></author><summary type="html"><![CDATA[This is part four of a five-part series of posts on compiling Amiga-specific C code on an (emulated) Amiga A1200. We’ll start with a machine with an empty hard drive, and over the series, build up to compiling an Amiga-specific program which uses the graphics library.]]></summary></entry><entry><title type="html">Compiling Amiga Specific C (Part 3 of 5)</title><link href="http://www.richardfawcett.net/2021/05/12/compiling-amiga-specific-c-part-3-of-5" rel="alternate" type="text/html" title="Compiling Amiga Specific C (Part 3 of 5)" /><published>2021-05-12T00:00:00+00:00</published><updated>2021-05-12T00:00:00+00:00</updated><id>http://www.richardfawcett.net/2021/05/12/compiling-amiga-specific-c-part-3-of-5</id><content type="html" xml:base="http://www.richardfawcett.net/2021/05/12/compiling-amiga-specific-c-part-3-of-5"><![CDATA[<p>This is part three of a five-part series of posts on compiling Amiga-specific C code on an (emulated) Amiga A1200. We’ll start with a machine with an empty hard drive, and over the series, build up to compiling an Amiga-specific program which use the graphics library.</p>

<ul>
  <li>Part 1 – <a href="/2021/04/30/compiling-amiga-specific-c-part-1-of-5.html">Introduction and motivation</a></li>
  <li>Part 2 – <a href="/2021/05/08/compiling-amiga-specific-c-part-2-of-5.html">Installing Workbench and necessary utilities</a></li>
  <li><strong>Part 3 – Installing VBCC and the Amiga include files</strong> (this part)</li>
  <li>Part 4 – <a href="/2021/05/19/compiling-amiga-specific-c-part-4-of-5.html">Compiling a simple Hello World program</a></li>
  <li>Part 5 – <a href="/2021/05/28/compiling-amiga-specific-c-part-5-of-5.html">Compiling some Amiga-specific code</a></li>
</ul>

<p>Let’s go!</p>

<h2 id="installing-vbcc-and-the-amiga-include-files">Installing VBCC and the Amiga include files</h2>

<p>My first task here was to choose a C compiler to use. There are a few available. Back in the day, I used DICE, but I heard that it’s pretty tricky to get setup, and it seems that the only binary version available is one that someone compiled from source. I ruled it out because I wanted something that “Just Works”, and this didn’t feel like it.</p>

<p>Some of the contenders were <strong>SAS/C</strong> (formerly known as Lattice), <strong>GCC</strong>, and <strong>VBCC</strong>. From <a href="https://www.warped.com/amiga/">the unofficial SAS/C page</a>, it looks like it’s reached end of life. While GCC offers a modern cross-platform toolchain, the latest version for Amiga is 2.7.2, which dates from 1998. <a href="http://sun.hasenbraten.de/vbcc/">VBCC</a> seems to still be in active development, is cross-platform, and can cross-compile for other platforms. Its relatively simple setup was another factor that led me to choose VBCC for this series.</p>

<p>For VBCC, you download the compiler for the host platform you’re compiling on, and then download one or more target files for the platforms you want to compile for. In our case, we’ll get both of them for m68k (the Motorola 680x0 series or processors).</p>

<p>We’ll also need to pick up the latest version of the NDK (Native Development Kit) for AmigaOS 3.x, which includes libraries and headers that we’ll need to compile our programs.</p>

<p>Here’s what we need to grab:</p>

<ul>
  <li>NDK 3.9 (<a href="http://www.haage-partner.de/download/AmigaOS/NDK39.lha">NDK39.lha</a>)</li>
  <li>VBCC m68k binaries (<a href="http://phoenix.owl.de/vbcc/2019-10-04/vbcc_bin_amigaos68k.lha">vbcc_bin_amigaos68k.lha</a>)</li>
  <li>VBCC target files for m68k (<a href="http://phoenix.owl.de/vbcc/2019-10-04/vbcc_target_m68k-amigaos.lha">vbcc_target_m68k-amigaos.lha</a>)</li>
</ul>

<p>Note that the VBCC target is for AmigaOS 2 / 3, and if you want to compile for e.g. Workbench 1.3, there’s a <a href="http://phoenix.owl.de/vbcc/2019-10-04/vbcc_target_m68k-kick13.lha">different download</a> for that.</p>

<p>Like we did last in the previous post, put the <code class="language-plaintext highlighter-rouge">.lha</code> files into the Amiga Forever Shared folder, and boot up the emulator.</p>

<p><img src="/assets/images/20210430_amiga/part3-01.png" alt="Image 01" /></p>

<p>This time, we’ll use the Shell do a lot of the work, rather than dragging and dropping in Workbench. Open a shell, and execute the following:</p>

<figure class="highlight"><pre><code class="language-plaintext" data-lang="plaintext">makedir c_dev
lha e Shared:NDK39.lha c_dev/
lha e Shared:vbcc_bin_amigaos68k.lha ram:
lha e Shared:vbcc_target_m68k-amigaos.lha ram:</code></pre></figure>

<p>Now, open the RAM disk with Workbench, and you’ll find two drawers in there. We’ll need to start with the binaries, so open up its drawer and double click the <code class="language-plaintext highlighter-rouge">Install</code> icon.</p>

<p>If you followed a long last week and installed the installer to C:, then this should begin the installation process automatically. Choose “Proceed With Install” then “Proceed”. When prompted for an install location, choose <code class="language-plaintext highlighter-rouge">System:c_dev</code>. That’s it, installation should have completed, and installed the VBCC binaries to <code class="language-plaintext highlighter-rouge">System:c_dev/vbcc</code>.</p>

<p>Now, we need to install the target files, so run the installer from in the other drawer in RAM. As we did with the binaries, kick off the installation. You’ll be prompted to locate the system headers directory. The default is <code class="language-plaintext highlighter-rouge">include:</code>, but we didn’t set up that assign to point to the includes we extracted from the NDK, so we’ll need to choose <code class="language-plaintext highlighter-rouge">System:c_dev/NDK_3.9/Include/include_h</code> here.</p>

<p><img src="/assets/images/20210430_amiga/part3-02.png" alt="Image 02" /></p>

<p>Once you’ve selected the right directory, hit “Proceed” again, and the installation finishes up pretty quickly. If you’re planning on shutting down your virtual machine now, you could delete the two drawers from the RAM disk to reclaim some memory.</p>

<p>We now have VBCC and the AmigaOS NDK installed on our machine! Next time, we’ll check everything’s working by compiling a simple “Hello world” program.</p>]]></content><author><name></name></author><summary type="html"><![CDATA[This is part three of a five-part series of posts on compiling Amiga-specific C code on an (emulated) Amiga A1200. We’ll start with a machine with an empty hard drive, and over the series, build up to compiling an Amiga-specific program which use the graphics library.]]></summary></entry><entry><title type="html">Compiling Amiga Specific C (Part 2 of 5)</title><link href="http://www.richardfawcett.net/2021/05/08/compiling-amiga-specific-c-part-2-of-5" rel="alternate" type="text/html" title="Compiling Amiga Specific C (Part 2 of 5)" /><published>2021-05-08T00:00:00+00:00</published><updated>2021-05-08T00:00:00+00:00</updated><id>http://www.richardfawcett.net/2021/05/08/compiling-amiga-specific-c-part-2-of-5</id><content type="html" xml:base="http://www.richardfawcett.net/2021/05/08/compiling-amiga-specific-c-part-2-of-5"><![CDATA[<p>This is part two of a five-part series of posts on compiling Amiga-specific C code on an (emulated) Amiga A1200. We’ll start with a machine with an empty hard drive, and over the series, build up to compiling an Amiga-specific program which uses the graphics library.</p>

<ul>
  <li>Part 1 – <a href="/2021/04/30/compiling-amiga-specific-c-part-1-of-5.html">Introduction and motivation</a></li>
  <li><strong>Part 2 – Installing Workbench and necessary utilities</strong> (this post)</li>
  <li>Part 3 – <a href="/2021/05/12/compiling-amiga-specific-c-part-3-of-5.html">Installing VBCC and the Amiga include files</a></li>
  <li>Part 4 – <a href="/2021/05/19/compiling-amiga-specific-c-part-4-of-5.html">Compiling a simple Hello World program</a></li>
  <li>Part 5 – <a href="/2021/05/28/compiling-amiga-specific-c-part-5-of-5.html">Compiling some Amiga-specific code</a></li>
</ul>

<p>Let’s go!</p>

<h2 id="installing-workbench-and-necessary-utilities">Installing Workbench and necessary utilities</h2>

<p>The first thing we’ll do is make fresh virtual machine, by copying the “Amiga 1200” machine provided as part of the Amiga Forever installation. In Amiga Forever, we can just right click on the Amiga 1200 machine and choose to “Create Copy”. Then, we click the edit button (the pencil on the toolbar) to bring up the settings for our VM.</p>

<p>The benefit of copying from the existing Amiga 1200 machine, is that we can just trust that most of the settings are correct. I’m just going to tweak the RAM on the “Configuration” tab.</p>

<p><img src="/assets/images/20210430_amiga/part2-01.png" alt="Image 01" /></p>

<p>The only change I’m making is to increase the Fast Memory from none to the maximum of 8 MB. I figured that if we’re going to be compiling code, we might need a bunch of memory, not really sure, but it doesn’t hurt to bump this up.</p>

<p>The other changes I made were on the “Media” tab, where we configure the disks and drives on our machine.</p>

<p><img src="/assets/images/20210430_amiga/part2-02.png" alt="Image 02" /></p>

<p>I clicked “Add…” to add a new disk, and in the resulting pop-up, chose a “Hard Disk Image File” and chose “Create Blank…”. I upped the capacity to 40 MB (from the default 10 MB) and left the other settings as they were.</p>

<p>Back on the “Add…” dialog, I chose to leave the location as “Embedded”. This means that our hard drive file and our VM settings will be kept packaged together in a single .rp9 file, making it easily portable between different machines. I might even store it on Google Drive so it’s available on all of my machines automatically.</p>

<p><img src="/assets/images/20210430_amiga/part2-03.png" alt="Image 03" /></p>

<p>“OK” the dialog, and it’s time to power up our machine with the play button on the toolbar.</p>

<p>The emulator simulates the real disk speed of an Amiga, so I chose to put my virtual floppy drive in “Turbo” mode to speed up things. This can apparently cause incompatibility with some software, so I’ll disable it if I have any weird behavior, but I suspect things will be just fine.</p>

<p><img src="/assets/images/20210430_amiga/part2-04.png" alt="Image 04" /></p>

<p>OK, so we’ve booted Workbench from floppy. It’s time to install it on our hard drive now. Here’s what we get when we boot up:</p>

<p><img src="/assets/images/20210430_amiga/part2-05.png" alt="Image 05" /></p>

<p>Select the DH0:NDOS volume (our hard disk) and choose “Format Disk…” from the Icons menu with the right mouse button.</p>

<p><img src="/assets/images/20210430_amiga/part2-06.png" alt="Image 06" /></p>

<p>I chose to call my volume “System” and then went through no less than three “are you sure” prompts before formatting began!</p>

<p>Now it’s time to install Workbench on our hard disk.</p>

<p>Workbench 3.1 came on a bunch of disks. We’ll need to rotate through them all to get Workbench installed on to the hard drive, starting with the Install disk. On the emulators bottom toolbar, on the floppy icon choose to insert a disk from file. If the open file dialog doesn’t default to the right place, try looking in <code class="language-plaintext highlighter-rouge">C:\Users\Public\Documents\Amiga Files\Shared\adf</code> for the file <code class="language-plaintext highlighter-rouge">amiga-os-310-install.adf</code>.</p>

<p>Open up the disk by double clicking on its icon in Workbench, then open the Install drawer, and finally double click on the English installer.</p>

<p><img src="/assets/images/20210430_amiga/part2-07.png" alt="Image 07" /></p>

<p>Throughout the process, you’ll be prompted several times to insert a volume in the drive. Just do that using the same technique we used to insert the Install disk. There’s usually no need to hit the “Retry” button …</p>

<p><img src="/assets/images/20210430_amiga/part2-08.png" alt="Image 08" /></p>

<p>Choose “Proceed”, and then “Install Release 3.1” before picking “Intermediate User”. Then click “Proceed” again to accept the defaults of “Install for Real” and log to “None”. It’s at this point that the installer makes a bad assumption. It wants to install to the “Workbench3.1:” partition, which is our read-only floppy drive.</p>

<p><img src="/assets/images/20210430_amiga/part2-09.png" alt="Image 09" /></p>

<p>Choose “No”, then hit “Show Drives” and choose the “DH0: System:” drive, before hitting Proceed.</p>

<p><img src="/assets/images/20210430_amiga/part2-10.png" alt="Image 10" /></p>

<p>Hit proceed a bunch of times. When prompted for keymaps, I choose “British”, but you do you at this point.</p>

<p>Workbench will now begin installation, prompting you to change disks a few times through the process. Just remember, each time you change, make sure you’re inserting the “310” disk file for Workbench 3.1. Having Turbo enabled (mentioned above) on the floppy drive really speeds things up here.</p>

<p>At the end of the process, we’re prompted to eject the final disk (again using the floppy disk icon at the bottom of the emulator) and then hit “Proceed” to reboot the system, which brings us up with our nice, shiny hard-drive intsalled Workbench.</p>

<p><img src="/assets/images/20210430_amiga/part2-11.png" alt="Image 11" /></p>

<p>We’re not quite done though. Power off the emulator. This will prompt you to commit changes to our hard drive file. It’s essential that you do leave this checked, as unchecking it would throw away all of the changes we’ve made to our hard drive this session … I think it’s only flushed to disk when we power down our machine.</p>

<p>Now, go back to the media settings for our machine, and choose to remove the boot Workbench 3.1 disk, and set the Shared dropdown box to “Local Folder”. This adds a DH1: drive to our system, that’s mapped to a location on our host PC.</p>

<p><img src="/assets/images/20210430_amiga/part2-12.png" alt="Image 12" /></p>

<p>From the Amiga Forever’s main window, choose “File -&gt; Open -&gt; Shared (Local Folder)” which will open the shared location in Windows Explorer. We’re now going to need to download a few utilities from the Internet that we’ll need to be able to install our C compiler in the next blog post.</p>

<p>Head over to <a href="http://aminet.net/">Aminet</a> and download the following files:</p>

<ul>
  <li><strong>lha.run</strong> – this is a self-extracting archive, that will install lha (a zip utility for Amiga that handles <code class="language-plaintext highlighter-rouge">.lha</code> files).</li>
  <li><strong>Installer-43_3.lha</strong> – this is an updated version of Workbench’s Installer utility, that’s needed to install some software that was developed after Workbench shipped, such as VBCC (the C compiler we’ll be installing next post).</li>
</ul>

<p>Place the downloaded files in the Shared folder, boot the emulator, and you should see those two files in the Shared drive from within the emulator. In order to see them, you’ll probably need to choose to show all files, once you’ve opened the Shared disk, like this:</p>

<p><img src="/assets/images/20210430_amiga/part2-13.png" alt="Image 13" /></p>

<p>Double click the <code class="language-plaintext highlighter-rouge">lha.run</code> file, and choose click OK when prompted for parameters. This will automatically extract the lha program.</p>

<p><img src="/assets/images/20210430_amiga/part2-14.png" alt="Image 14" /></p>

<p>Close the output window. Disappointingly, you won’t see the new files in the Shared folder, but you have to make allowances for a 30-year-old operating system. From the menu, choose “Window -&gt; Update” and then “Window -&gt; Cleanup”. As we’re running on an Amiga 1200, which has a Motorola 68020 processor, the file we’re interested in is <code class="language-plaintext highlighter-rouge">lha_68020</code>. Open the System disk, choose to show all files, then open the C drawer. This is where Amiga stores commands, and it’s part of the path. Rename the <code class="language-plaintext highlighter-rouge">lha_68020</code> file to <code class="language-plaintext highlighter-rouge">lha</code> (using the “Icons -&gt; Rename…” menu option) and then drag it across to the C drawer.</p>

<p>If you lose any windows during the process, this button at the top right of every window will either sent it to the back (if it’s the front-most window) or bring it to the front otherwise.</p>

<p><img src="/assets/images/20210430_amiga/part2-15.png" alt="Image 15" /></p>

<p>We now need to install the new Installer, but need to unpack it first. We’ll do that from the Amiga Shell, which we can find in the System drawer in the System disk.</p>

<p>Enter the commands <code class="language-plaintext highlighter-rouge">cd Shared:</code> and then <code class="language-plaintext highlighter-rouge">lha e Installer-43_3.lha</code> (the “e” means extract). You should see that Installer has been extracted to a new drawer <code class="language-plaintext highlighter-rouge">Installer43_3</code>, like this:</p>

<p><img src="/assets/images/20210430_amiga/part2-16.png" alt="Image 16" /></p>

<p>Finally, just like we did with <code class="language-plaintext highlighter-rouge">lha</code>, we need to copy <code class="language-plaintext highlighter-rouge">Installer</code> (from the newly created <code class="language-plaintext highlighter-rouge">Installer43_3</code> drawer) to <code class="language-plaintext highlighter-rouge">C:</code>, like this:</p>

<p><img src="/assets/images/20210430_amiga/part2-17.png" alt="Image 17" /></p>

<p>That’s it … we have everything we need to get on with installing our C compiler next time.</p>

<p>This has been a super long post, but I think you can probably get everything done in maybe 15 minutes. Remember to save the changes to our hard drive when you power down the virtual machine!</p>]]></content><author><name></name></author><summary type="html"><![CDATA[This is part two of a five-part series of posts on compiling Amiga-specific C code on an (emulated) Amiga A1200. We’ll start with a machine with an empty hard drive, and over the series, build up to compiling an Amiga-specific program which uses the graphics library.]]></summary></entry><entry><title type="html">Compiling Amiga Specific C (Part 1 of 5)</title><link href="http://www.richardfawcett.net/2021/04/30/compiling-amiga-specific-c-part-1-of-5" rel="alternate" type="text/html" title="Compiling Amiga Specific C (Part 1 of 5)" /><published>2021-04-30T00:00:00+00:00</published><updated>2021-04-30T00:00:00+00:00</updated><id>http://www.richardfawcett.net/2021/04/30/compiling-amiga-specific-c-part-1-of-5</id><content type="html" xml:base="http://www.richardfawcett.net/2021/04/30/compiling-amiga-specific-c-part-1-of-5"><![CDATA[<p>This is part one of a five-part series of posts on compiling Amiga-specific C code on an (emulated) Amiga A1200. We’ll start with a machine with an empty hard drive, and over the series, build up to compiling an Amiga-specific program which uses the graphics library.</p>

<ul>
  <li><strong>Part 1 – Introduction and motivation</strong> (this post)</li>
  <li>Part 2 – <a href="/2021/05/08/compiling-amiga-specific-c-part-2-of-5.html">Installing Workbench and necessary utilities</a></li>
  <li>Part 3 – <a href="/2021/05/12/compiling-amiga-specific-c-part-3-of-5.html">Installing VBCC and the Amiga include files</a></li>
  <li>Part 4 – <a href="/2021/05/19/compiling-amiga-specific-c-part-4-of-5.html">Compiling a simple Hello World program</a></li>
  <li>Part 5 – <a href="/2021/05/28/compiling-amiga-specific-c-part-5-of-5.html">Compiling some Amiga-specific code</a></li>
</ul>

<p>Let’s go!</p>

<h2 id="introduction-and-motivation">Introduction and motivation</h2>

<p>Way back in history, when I was maybe six or seven years old, I got my first computer: a Sinclair ZX-81 with its somewhat limited 1kB of RAM. I was hooked immediately. I quickly managed to get myself a 16kB RAM expansion pack, and taught myself BASIC by typing in listings from computer mags and Usborne books, marveling at the way I could get this machine to do what I wanted.</p>

<p>By the early 1990s, following a spell with a Commodore Plus Four, I moved up to a Commodore Amiga A500+ and shortly after that to an A1200 with a 20MB internal hard drive. Sure, the Amiga was a great gaming machine, but it’s the machine I really learned to program on. I learned the ins and outs of the Amiga’s shell scripting language, and really honed my BASIC skills with AMOS Professional.</p>

<p>My first foray into C came in 1994, when Amiga Shopper magazine included a cut down version of DICE (which I think stands for Dillon’s Integrated C Environment), and a sample of a couple of chapters of Cliff Ramshaw’s book: Complete Amiga C. The coverdisk was missing the Amiga include files, and as far as I can remember, didn’t get as far as making Amiga library calls. I needed more, so I sent away in the post to buy the full book, which came with the full version of DICE including the official Amiga header files.</p>

<p>Now, fast forward over 25 years to 2021. I’ve just moved house, and as part of clearing things out I went through all my old computer books, including Complete Amiga C. I flicked through the old musty pages, and it really rekindled a yearning to get back to playing around on an Amiga. Sadly, my childhood machines have disappeared … I genuinely have no recollection of where they went, so I started investigating emulation options. I start with <a href="https://www.winuae.net/">WinUAE</a> but the real limitation is that it doesn’t come with Kickstart ROM images, which together with the Workbench software, make up AmigaOS. The only legal ways that I know of to get the ROM images are to either extract them from your own Amiga using a tool like TransROM (included with WinUAE), or to buy them. I also found WinUAE to be a bit of a pain to configure, and not really very inviting for an emulator novice.</p>

<p>Just take a look at what WinUAE present you with after first loading. It’s pretty overwhelming.</p>

<p><img src="/assets/images/20210430_amiga/WinUAE.png" alt="WinUAE" title="WinUAE" /></p>

<p>That’s when I heard about <a href="https://www.amigaforever.com/">Amiga Forever</a>. It’s essentially a really nice warpper around WinUAE, that package up machines and disk images together into .rp9 files. But what else is really cool is that it comes complete with licensed Kickstart ROM and Workbench disk images. Seemed like a pretty easy snap purchase to me.</p>

<p>In comparison to WinUAE, you can see that Amiga Forever has a bunch of predefined machine images already created and ready for you to boot up. Much easier to get started with.</p>

<p><img src="/assets/images/20210430_amiga/Amiga-Forever.png" alt="Amiga Forever" title="Amiga Forever" /></p>

<p>So, what I’m going to do over the next four posts, is get an Amiga 1200 setup with a virtual hard disk, a compiler installed, and compiling some C code which makes use of AmigaOS kernel calls. You could grab Amiga Forever yourself and follow along, or (if you can source Workbench and ROM images) do the same for free with WinUAE. Or just read along and relive the sheer joy that I’m having working with this “ancient” machine.</p>

<p>It’s going to be a fun ride.</p>]]></content><author><name></name></author><summary type="html"><![CDATA[This is part one of a five-part series of posts on compiling Amiga-specific C code on an (emulated) Amiga A1200. We’ll start with a machine with an empty hard drive, and over the series, build up to compiling an Amiga-specific program which uses the graphics library.]]></summary></entry><entry><title type="html">Capturing Mutable Variables is Bad</title><link href="http://www.richardfawcett.net/2021/04/24/capturing-mutables-variables-is-bad" rel="alternate" type="text/html" title="Capturing Mutable Variables is Bad" /><published>2021-04-24T00:00:00+00:00</published><updated>2021-04-24T00:00:00+00:00</updated><id>http://www.richardfawcett.net/2021/04/24/capturing-mutables-variables-is-bad</id><content type="html" xml:base="http://www.richardfawcett.net/2021/04/24/capturing-mutables-variables-is-bad"><![CDATA[<p>I once got caught out by a really hard to spot bug in C#. I was automatically capturing a variable in a lambda, and that variable was mutable … it was a value defined in a for loop. I’ve created the simplest possible repro to illustrate the problem:</p>

<figure class="highlight"><pre><code class="language-csharp" data-lang="csharp"><span class="k">class</span> <span class="nc">Program</span> <span class="p">{</span>
	<span class="k">static</span> <span class="k">void</span> <span class="nf">Main</span><span class="p">(</span><span class="kt">string</span><span class="p">[]</span> <span class="n">args</span><span class="p">)</span> <span class="p">{</span>
		<span class="kt">int</span> <span class="n">max</span> <span class="p">=</span> <span class="m">5</span><span class="p">;</span>
		<span class="kt">var</span> <span class="n">actions</span> <span class="p">=</span> <span class="k">new</span> <span class="n">System</span><span class="p">.</span><span class="n">Action</span><span class="p">[</span><span class="n">max</span><span class="p">];</span>

		<span class="k">for</span> <span class="p">(</span><span class="kt">int</span> <span class="n">i</span> <span class="p">=</span> <span class="m">0</span><span class="p">;</span> <span class="n">i</span> <span class="p">&lt;</span> <span class="n">max</span><span class="p">;</span> <span class="n">i</span><span class="p">++)</span> <span class="p">{</span>
			<span class="n">actions</span><span class="p">[</span><span class="n">i</span><span class="p">]</span> <span class="p">=</span> <span class="p">()</span> <span class="p">=&gt;</span> <span class="n">System</span><span class="p">.</span><span class="n">Console</span><span class="p">.</span><span class="nf">WriteLine</span><span class="p">(</span><span class="s">"I am action "</span> <span class="p">+</span> <span class="n">i</span><span class="p">);</span>
		<span class="p">}</span>

		<span class="k">foreach</span> <span class="p">(</span><span class="kt">var</span> <span class="n">a</span> <span class="k">in</span> <span class="n">actions</span><span class="p">)</span> <span class="n">a</span><span class="p">.</span><span class="nf">Invoke</span><span class="p">();</span>
	<span class="p">}</span>
<span class="p">}</span></code></pre></figure>

<p>(Don’t worry, my actual bug was more complex than this, so please excuse me for taking some time to figure it out when it happened).</p>

<p>I’d naively expected that each action would print its number, 0 to 4. However, when you run this, you get “I am action 5” printed five times. That’s because at the time the Action is executed, then the variable i is actually set to 5 (which is when the condition to exit the loop is satisfied).</p>

<p>In order to get this to work as expected, we need to create a new variable to hold the loop index … a variable that is never mutated. We can do this inside the loop definition, because each iteration of the loop essentially gets its own variable:</p>

<figure class="highlight"><pre><code class="language-csharp" data-lang="csharp"><span class="k">class</span> <span class="nc">Program</span> <span class="p">{</span>
	<span class="k">static</span> <span class="k">void</span> <span class="nf">Main</span><span class="p">(</span><span class="kt">string</span><span class="p">[]</span> <span class="n">args</span><span class="p">)</span> <span class="p">{</span>
		<span class="kt">int</span> <span class="n">max</span> <span class="p">=</span> <span class="m">5</span><span class="p">;</span>
		<span class="kt">var</span> <span class="n">actions</span> <span class="p">=</span> <span class="k">new</span> <span class="n">System</span><span class="p">.</span><span class="n">Action</span><span class="p">[</span><span class="n">max</span><span class="p">];</span>

		<span class="k">for</span> <span class="p">(</span><span class="kt">int</span> <span class="n">i</span> <span class="p">=</span> <span class="m">0</span><span class="p">;</span> <span class="n">i</span> <span class="p">&lt;</span> <span class="n">max</span><span class="p">;</span> <span class="n">i</span><span class="p">++)</span> <span class="p">{</span>
			<span class="kt">var</span> <span class="n">z</span> <span class="p">=</span> <span class="n">i</span><span class="p">;</span>
			<span class="n">actions</span><span class="p">[</span><span class="n">i</span><span class="p">]</span> <span class="p">=</span> <span class="p">()</span> <span class="p">=&gt;</span> <span class="n">System</span><span class="p">.</span><span class="n">Console</span><span class="p">.</span><span class="nf">WriteLine</span><span class="p">(</span><span class="s">"I am action "</span> <span class="p">+</span> <span class="n">z</span><span class="p">);</span>
		<span class="p">}</span>

		<span class="k">foreach</span> <span class="p">(</span><span class="kt">var</span> <span class="n">a</span> <span class="k">in</span> <span class="n">actions</span><span class="p">)</span> <span class="n">a</span><span class="p">.</span><span class="nf">Invoke</span><span class="p">();</span>
	<span class="p">}</span>
<span class="p">}</span></code></pre></figure>

<p>As expected, when this fixed up version runs, each <code class="language-plaintext highlighter-rouge">Action</code> correctly keeps track of its own number and outputs it to the console.</p>

<p>I then compared this to Java, which actually gives you some safety right out of the box. If you try to capture a mutable variable, Java code will not compile, and you get an error <code class="language-plaintext highlighter-rouge">java: local variables referenced from a lambda expression must be final or effectively final</code>. I especially like the “effectively final” bit, which means it’s not actually <em>required</em> to mark your captured variable as <code class="language-plaintext highlighter-rouge">final</code>, but as long as it’s not modified after it’s initialized, Java is ok with it. Here’s the Java code for comparison:</p>

<figure class="highlight"><pre><code class="language-java" data-lang="java"><span class="kd">public</span> <span class="kd">class</span> <span class="nc">Main</span> <span class="o">{</span>
	<span class="kd">public</span> <span class="kd">static</span> <span class="kt">void</span> <span class="nf">main</span><span class="o">(</span><span class="nc">String</span><span class="o">[]</span> <span class="n">args</span><span class="o">)</span> <span class="o">{</span>
		<span class="kt">int</span> <span class="n">max</span> <span class="o">=</span> <span class="mi">5</span><span class="o">;</span>
		<span class="kt">var</span> <span class="n">actions</span> <span class="o">=</span> <span class="k">new</span> <span class="nc">Runnable</span><span class="o">[</span><span class="n">max</span><span class="o">];</span>

		<span class="k">for</span> <span class="o">(</span><span class="kt">int</span> <span class="n">i</span> <span class="o">=</span> <span class="mi">0</span><span class="o">;</span> <span class="n">i</span> <span class="o">&lt;</span> <span class="n">max</span><span class="o">;</span> <span class="n">i</span><span class="o">++)</span> <span class="o">{</span>
			<span class="kt">int</span> <span class="n">z</span> <span class="o">=</span> <span class="n">i</span><span class="o">;</span>
			<span class="n">actions</span><span class="o">[</span><span class="n">i</span><span class="o">]</span> <span class="o">=</span> <span class="o">()</span> <span class="o">-&gt;</span> <span class="nc">System</span><span class="o">.</span><span class="na">out</span><span class="o">.</span><span class="na">println</span><span class="o">(</span><span class="s">"I am action "</span> <span class="o">+</span> <span class="n">z</span><span class="o">);</span>
		<span class="o">}</span>

		<span class="k">for</span> <span class="o">(</span><span class="nc">Runnable</span> <span class="n">a</span> <span class="o">:</span> <span class="n">actions</span><span class="o">)</span> <span class="n">a</span><span class="o">.</span><span class="na">run</span><span class="o">();</span>
	<span class="o">}</span>
<span class="o">}</span></code></pre></figure>

<p>So, what if I do actually want to have all the actions in the Java version print 5, the variable at the end of the loop? Well, the correct way to capture a mutable variable is actually by a parameter to the lambda … it’s much easier to rationalize about stuff then. Like this:</p>

<figure class="highlight"><pre><code class="language-java" data-lang="java"><span class="kn">import</span> <span class="nn">java.util.function.IntConsumer</span><span class="o">;</span>

<span class="kd">public</span> <span class="kd">class</span> <span class="nc">Main</span> <span class="o">{</span>
	<span class="kd">public</span> <span class="kd">static</span> <span class="kt">void</span> <span class="nf">main</span><span class="o">(</span><span class="nc">String</span><span class="o">[]</span> <span class="n">args</span><span class="o">)</span> <span class="o">{</span>
		<span class="kt">int</span> <span class="n">max</span> <span class="o">=</span> <span class="mi">5</span><span class="o">;</span>
		<span class="kt">var</span> <span class="n">actions</span> <span class="o">=</span> <span class="k">new</span> <span class="nc">IntConsumer</span><span class="o">[</span><span class="n">max</span><span class="o">];</span>

		<span class="kt">int</span> <span class="n">i</span> <span class="o">=</span> <span class="mi">0</span><span class="o">;</span>
		<span class="k">for</span> <span class="o">(</span><span class="n">i</span> <span class="o">=</span> <span class="mi">0</span><span class="o">;</span> <span class="n">i</span> <span class="o">&lt;</span> <span class="n">max</span><span class="o">;</span> <span class="n">i</span><span class="o">++)</span> <span class="o">{</span>
			<span class="n">actions</span><span class="o">[</span><span class="n">i</span><span class="o">]</span> <span class="o">=</span> <span class="o">(</span><span class="n">x</span><span class="o">)</span> <span class="o">-&gt;</span> <span class="nc">System</span><span class="o">.</span><span class="na">out</span><span class="o">.</span><span class="na">println</span><span class="o">(</span><span class="s">"I am action "</span> <span class="o">+</span> <span class="n">x</span><span class="o">);</span>
		<span class="o">}</span>

		<span class="k">for</span> <span class="o">(</span><span class="kt">var</span> <span class="n">a</span> <span class="o">:</span> <span class="n">actions</span><span class="o">)</span> <span class="n">a</span><span class="o">.</span><span class="na">accept</span><span class="o">(</span><span class="n">i</span><span class="o">);</span>
	<span class="o">}</span>
<span class="o">}</span></code></pre></figure>

<p>That’s a weird example, but you get the picture. Instead of using <code class="language-plaintext highlighter-rouge">Runnable</code>, I’ve used <code class="language-plaintext highlighter-rouge">IntConsumer</code> and pass in the <code class="language-plaintext highlighter-rouge">int</code> at the time we execute the lambda, rather than have it being captured. The final snippet above will have the output of my initial bugged C#, i.e. “I am action 5” repeated five times.</p>

<p>I’m a big fan of the way Java prevents you from shooting yourself in the foot like this.</p>]]></content><author><name></name></author><summary type="html"><![CDATA[I once got caught out by a really hard to spot bug in C#. I was automatically capturing a variable in a lambda, and that variable was mutable … it was a value defined in a for loop. I’ve created the simplest possible repro to illustrate the problem:]]></summary></entry></feed>