<?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="https://blowback.github.io/antforth/feed.xml" rel="self" type="application/atom+xml" /><link href="https://blowback.github.io/antforth/" rel="alternate" type="text/html" /><updated>2026-04-04T17:19:25+00:00</updated><id>https://blowback.github.io/antforth/feed.xml</id><title type="html">AntForth dev diary</title><subtitle>A Forth interpreter for the MicroBeast Z80 computer</subtitle><entry><title type="html">Dictionary and hash table</title><link href="https://blowback.github.io/antforth/2026/04/06/dictionary-hash-table.html" rel="alternate" type="text/html" title="Dictionary and hash table" /><published>2026-04-06T00:00:00+00:00</published><updated>2026-04-06T00:00:00+00:00</updated><id>https://blowback.github.io/antforth/2026/04/06/dictionary-hash-table</id><content type="html" xml:base="https://blowback.github.io/antforth/2026/04/06/dictionary-hash-table.html"><![CDATA[<p>We’re now starting Epic 2, which is all about getting us to a functional AntForth interpreter.</p>

<p>The first story (2.1) is about the dictionary and has table. The dictionary is where 
all our Forth word definitions are stored, so that they can be looked up when they’re 
used. Traditionally this is a single-linked list, which is simple to implement but gets 
slower and slower the more words are added.</p>

<p>We’ve specified that our interpreter should use a more optimised hash-based approach. Effectively
we keep 64 separate dictionaries, and decide which dictionary a word belongs to by using some 
simple mathematical function (the “has function”) on its name.</p>

<p>The hash function needs to be fast and easy to implement on a z80 - we’re using a rotating XOR 
 hash function and 64 mini-dictionaries to meet these needs, but really any function that 
distributes words across the dictionaries evenly will do.</p>

<p>We’ve already been using this hash lookup approach, but up to now the hashes have been 
built at assembly time using sjasmplus’ Lua scripting ability.</p>

<p>In this sprint, we’ll be building:</p>

<ul>
  <li>a z80 native version of the hash function</li>
  <li>the <code class="language-plaintext highlighter-rouge">FIND</code> word, for looking up word names in the dictionary</li>
  <li>the <code class="language-plaintext highlighter-rouge">COUNT</code> word, to tell us the length of byte counted strings (like the names of words 
in the dictionary)</li>
</ul>

<h2 id="hashasm">hash.asm</h2>

<p>The runtime hashing algorithm is pretty simple:</p>

<p><img src="/antforth/assets/images/2026-04-06/hash.png" alt="hash algorithm" /></p>

<p>It converts the string to uppercase, XORs the current hash value (initial hash value is 
zero) with the uppercase character, and rotates the current hash value left one bit. 
Remember that <code class="language-plaintext highlighter-rouge">RLC A</code> is an 8 bit rotate as the comment makes clear.</p>

<p>The upper-case conversion code was originally explicit in here, with a whinging comment 
about there being duplicate code in dictionary.asm and dire warnings about keeping the 
two synchronized - so I asked it to replace all instances of that code with a macro 
<code class="language-plaintext highlighter-rouge">UPPER</code>.</p>

<h2 id="dictionaryasm">dictionary.asm</h2>

<p><code class="language-plaintext highlighter-rouge">COUNT</code> also gets quite a simple implementation:</p>

<p><img src="/antforth/assets/images/2026-04-06/count.png" alt="count algorithm" /></p>

<p>When a word is stored in a forth dictionary, its name is a byte counted string (the 
string is preceded by a byte that is the count of the number of its characters), but 
a couple of the top bits of the length byte are used for other purposes, so this 
routine is basically masking those extra bits out, so we can treat it as a regular 
byte-counted string.</p>

<p>The implementation of <code class="language-plaintext highlighter-rouge">FIND</code> is also here, but it’s a bit too long to include in 
this blog post.</p>

<p>In essence what it’s doing is:</p>

<ul>
  <li>hash the name we’re looking up to identify the relevant dictionary “chain” (mini-dictionary)</li>
  <li>search that chain for a match, searching by length first, then a full name comparison</li>
  <li>if it’s found return the “xt” (execution token) and a +1 or -1 flag depending on whether the word is “immediate” or not (more on this later)</li>
  <li>else return the original address with a zero pushed on top if there was no match.</li>
</ul>

<p>The <code class="language-plaintext highlighter-rouge">FIND</code> routine uses some static storage, so it’s not re-entrant. As we’re 
single threaded by design, this shouldn’t be an issue.</p>

<h2 id="testing">Testing</h2>

<p>All tests pass, and the manual <code class="language-plaintext highlighter-rouge">KEY</code> test still works.</p>]]></content><author><name></name></author><summary type="html"><![CDATA[We’re now starting Epic 2, which is all about getting us to a functional AntForth interpreter.]]></summary></entry><entry><title type="html">Console IO</title><link href="https://blowback.github.io/antforth/2026/04/05/console-io.html" rel="alternate" type="text/html" title="Console IO" /><published>2026-04-05T00:00:00+00:00</published><updated>2026-04-05T00:00:00+00:00</updated><id>https://blowback.github.io/antforth/2026/04/05/console-io</id><content type="html" xml:base="https://blowback.github.io/antforth/2026/04/05/console-io.html"><![CDATA[<p>On to the next BMAD task: 1.5 - console I/O primitives</p>

<p>In this sprint, we’ll be building:</p>

<ul>
  <li>output primitives <code class="language-plaintext highlighter-rouge">EMIT</code>, <code class="language-plaintext highlighter-rouge">CR</code>, <code class="language-plaintext highlighter-rouge">SPACE</code> and <code class="language-plaintext highlighter-rouge">SPACES</code></li>
  <li>input primitives <code class="language-plaintext highlighter-rouge">KEY</code> and <code class="language-plaintext highlighter-rouge">KEY?</code></li>
</ul>

<p>These should all be trivial wrappers around BDOS calls to do the actual work.
These functions will be essential when it comes to building the full outer interpreter 
that will make AntForth interactive!</p>

<p>To get started we <code class="language-plaintext highlighter-rouge">/bmad-bmm-create-story 1-5</code> and review the story. Straight 
away Claude has flagged that <code class="language-plaintext highlighter-rouge">KEY</code> and <code class="language-plaintext highlighter-rouge">KEY?</code> can’t be tested automatically,
which is fair, but I asked for a separate test executable to be provided so 
I can test them nmanually:</p>

<div class="language-plaintext highlighter-rouge"><div class="highlight"><pre class="highlight"><code>please make sure that a separate manual test executable is provided so that 
I can test KEY and KEY? - just a prog that echos what I type is fine, ctrl-c to exit.
</code></pre></div></div>

<p>With the updated plan in place we can <code class="language-plaintext highlighter-rouge">/bmad-bmm-dev-story 1-5</code>, 
followed by <code class="language-plaintext highlighter-rouge">/bmad-bmm-code-review 1-5</code>.</p>

<p>Code review found some minor test coverage gaps, which were quickly fixed.</p>

<h2 id="ioasm">io.asm</h2>

<p>All the new console stuff, unsurprisingly, is in io.asm. The output routines are all 
variations on the same basic theme:</p>

<p><img src="/antforth/assets/images/2026-04-05/emit.png" alt="console output" /></p>

<p>Similarly the input routines:</p>

<p><img src="/antforth/assets/images/2026-04-05/key.png" alt="console input" /></p>

<h2 id="testing">Testing</h2>

<p><code class="language-plaintext highlighter-rouge">make test</code> passes, although the hard-coded test-thread in antforth.asm is starting 
to get a bit wild and overgrown - the sooner we have a proper interpreter the better!</p>

<p>Claude did make us a manual test <code class="language-plaintext highlighter-rouge">key_test.com</code> which we have to run ourselves:</p>

<p><img src="/antforth/assets/images/2026-04-05/typing.png" alt="typing test" /></p>

<p>Although, now I look at the source code:</p>

<pre><code class="language-z80">; ================================================
; test_key.asm — Manual test for KEY / KEY? primitives
; Echoes typed characters. Ctrl-C to exit.
; Part of antforth — ANS Forth for MicroBeast Z80
; ================================================

        INCLUDE "constants.asm"

        ORG     TPA_START       ; 0x0100

echo_loop:
        LD      C, C_READ       ; BDOS function 1: read console (blocking, echoes)
        CALL    BDOS_ENTRY      ; A = character typed
        CP      0x03            ; Ctrl-C?
        JR      NZ, echo_loop   ; No — keep looping (char already echoed by BDOS)
        LD      C, P_TERMCPM    ; BDOS function 0: exit to CP/M
        JP      BDOS_ENTRY

</code></pre>

<p>That just tests BDOS and doesn’t test any of our stuff at all!</p>

<p>Back to Claude then:</p>

<div class="language-plaintext highlighter-rouge"><div class="highlight"><pre class="highlight"><code>❯ test_key.asm does not test our KEY or KEY? implementations, it calls the BDOS routine directly.
as a test therefore, it is entirely useless. Write a proper test that actually exercises the code-under-test!!

● You're absolutely right — test_key.asm just calls BDOS directly and doesn't exercise our KEY or KEY?
code words at all. I need to build a proper Forth-threaded test program. Let me check what infrastructure is needed.
</code></pre></div></div>

<p>This kind of thing is unfortunately far too commmon, even BMAD couldn’t save us this time. It’s 
the perfect example of what the human responsibilities are in this partnership!</p>

<p>Here’s Claude making amends:</p>

<p><img src="/antforth/assets/images/2026-04-05/test_key2.png" alt="typing test amends" /></p>

<p>Checking the source code, this time we have got something more substantial:</p>

<p><img src="/antforth/assets/images/2026-04-05/test_key_code2.png" alt="typing test 2 code" /></p>

<p>This is what it looks like when you run it:</p>

<p><img src="/antforth/assets/images/2026-04-05/typing2.png" alt="typing test 2" /></p>

<p>This would be a great topic to raise in a sprint retrospective, and as we’ve just finished Epic 1 
you’ll see that the next step in <code class="language-plaintext highlighter-rouge">sprint-status.yaml</code> is an optional retrospective. Let’s take that 
option!</p>

<p>First <code class="language-plaintext highlighter-rouge">/reset</code> claude code and then <code class="language-plaintext highlighter-rouge">/bmad-bmm-retrospective epic 1</code>. This kicks off a long, 
interactive session where the LLM adopts several personas (developer, architect, tester) 
simultaneously. You get to ask them challenging questions, and they bicker about whose 
fault it is. Stay alert, sometimes they’ll conclude that it’s <em>your</em> fault. It is a frankly 
hilarious experience, eerily reminiscent of a real-world retro. The end result is that your 
PRD and architecture documents will get updated with the lessons learned. In extreme cases 
a “course change” may be scheduled, which is usually a single extra story, but can in theory 
be a whole new emergency epic if the wheels have really come  off.</p>

<p>The whole chat is too long to include here, but this was what I kicked off with:</p>

<div class="language-plaintext highlighter-rouge"><div class="highlight"><pre class="highlight"><code>❯ It's a solid foundation, and it's exciting to see the functionality coming together. But let's not pat ourselves on the back about testing. The single test thread in antforth.asm is becoming unwieldy and we urgently need to replace it with something more modular - I guess we'll have to wait for the outer
interpreter to be available. MORE CONCERNING for me is that I asked for a manual test harness for `KEY` and `KEY?` and was given code that didn't excercise *any* of the code under test - it just called some BDOS routines directly. This is an unforgiveable oversight, and it must not happen again.
</code></pre></div></div>

<p>And you can see the <a href="https://github.com/blowback/antforth/blob/main/_bmad-output/implementation-artifacts/epic-1-retro-2026-04-04.md">resulting document</a> with the conclusions they came to.</p>

<p>Which all seems sensible. On to epic 2!</p>]]></content><author><name></name></author><summary type="html"><![CDATA[On to the next BMAD task: 1.5 - console I/O primitives]]></summary></entry><entry><title type="html">Arithmetic, logic and relational primitives</title><link href="https://blowback.github.io/antforth/2026/04/04/arithmetic-logic.html" rel="alternate" type="text/html" title="Arithmetic, logic and relational primitives" /><published>2026-04-04T00:00:00+00:00</published><updated>2026-04-04T00:00:00+00:00</updated><id>https://blowback.github.io/antforth/2026/04/04/arithmetic-logic</id><content type="html" xml:base="https://blowback.github.io/antforth/2026/04/04/arithmetic-logic.html"><![CDATA[<p>On to the next BMAD task: 1.4 - arithmetic, logic and relational operators.</p>

<p>In this sprint, we’ll be building:</p>

<ul>
  <li>arithmetic operators: <code class="language-plaintext highlighter-rouge">+</code>, <code class="language-plaintext highlighter-rouge">-</code>, <code class="language-plaintext highlighter-rouge">*</code>, <code class="language-plaintext highlighter-rouge">/</code>, <code class="language-plaintext highlighter-rouge">MOD</code>, <code class="language-plaintext highlighter-rouge">/MOD</code></li>
  <li>logical operators: <code class="language-plaintext highlighter-rouge">AND</code>, <code class="language-plaintext highlighter-rouge">OR</code>, <code class="language-plaintext highlighter-rouge">XOR</code>, <code class="language-plaintext highlighter-rouge">INVERT</code>, <code class="language-plaintext highlighter-rouge">LSHIFT</code>, <code class="language-plaintext highlighter-rouge">RSHIFT</code></li>
  <li>relational operators: <code class="language-plaintext highlighter-rouge">=</code>, <code class="language-plaintext highlighter-rouge">&lt;</code>, <code class="language-plaintext highlighter-rouge">&gt;</code>, <code class="language-plaintext highlighter-rouge">0=</code>, <code class="language-plaintext highlighter-rouge">0&lt;</code>, <code class="language-plaintext highlighter-rouge">U&lt;</code></li>
</ul>

<p>That’s a lot of very useful functionality that will significantly boost the 
abilities of our implementation. Most of these will be relatively trivial 
wrappers around the equivalent z80 machine code instructions, but some of them 
will be a little more involved: anything relating to multiplication and division 
in particular.</p>

<p>Claude will implement tests for each of these, adding them to the current hard-coded 
test thread in antforth.asm.</p>

<p>To get started we <code class="language-plaintext highlighter-rouge">/bmad-bmm-create-story 1-4</code>, review the story, then
<code class="language-plaintext highlighter-rouge">/bmad-bmm-dev-story 1-4</code>, followed by <code class="language-plaintext highlighter-rouge">/bmad-bmm-code-review 1-4</code>.</p>

<p>Code review threw up a few missing tests, but nothing more serious. We let Claude go ahead 
and fix those himself (it’s worth noting that the code review is an <em>adversarial</em> code review, 
so the reviewer MUST find some bones to pick - he’s not allowed to just wave work through).</p>

<h2 id="arithmeticasm">arithmetic.asm</h2>

<h3 id="add-and-subtract">Add and subtract</h3>

<p>Let’s start with a couple of those simple wrappers:</p>

<p><img src="/antforth/assets/images/2026-04-04/simple_maths.png" alt="add and subtract" /></p>

<p>Nothing too complex in there: BC is our top of stack, and we POP HL to get next-top-of-stack, 
then do an add or a subtract and leave the result in top-of-stack (BC). Simple.</p>

<h3 id="multiplication">Multiplication</h3>

<p>Next up we have <code class="language-plaintext highlighter-rouge">*</code> which multiplies two signed 16-bit words and returns the signed 16-bit 
result (i.e. the result is truncated from the maximum possible 32-bit result):</p>

<p><img src="/antforth/assets/images/2026-04-04/star.png" alt="multiply" /></p>

<p>This is the classic “shift-and-add” z80 multiplication — the binary equivalent of long multiplication by hand. The Z80 has no multiply instruction, so this is done in software:</p>

<ol>
  <li>Setup: n2 is already in BC (top-of-stack), get n1 in DE, set HL = 0</li>
  <li>
    <p>Main loop (16 iterations, one per bit of the multiplier):</p>
  </li>
  <li>
    <p>a. <code class="language-plaintext highlighter-rouge">ADD HL, HL</code> — left-shift the accumulator. This is the positional weighting — each previously-added value gets shifted up one place, just like when you indent each row in long multiplication.</p>
  </li>
  <li>
    <p>b. <code class="language-plaintext highlighter-rouge">SLA C / RL B</code>  — left-shift BC (the multiplier). The most significant bit falls out into carry. This examines the multiplier bits from MSB to LSB.</p>
  </li>
  <li>
    <p>c. <code class="language-plaintext highlighter-rouge">JR NC, .mul_skip</code>  — if that bit was 0, skip the add.</p>
  </li>
  <li>d. <code class="language-plaintext highlighter-rouge">ADD HL, DE</code> — if the bit was 1, add the multiplicand to the accumulator.</li>
  <li>Finish: copy result (HL) into top-of-stack (BC) and restore Instruction Pointer (DE) before NEXT</li>
</ol>

<h4 id="why-msb-first">Why MSB-first?</h4>

<p>This scans the multiplier from the top bit down, which avoids needing to shift the multiplicand. Instead, the accumulator is shifted left each iteration, which has the same effect. Compare the two equivalent approaches:</p>

<ul>
  <li>LSB-first: shift multiplicand left each step, add to fixed accumulator</li>
  <li>MSB-first (used here): shift accumulator left each step, add fixed multiplicand</li>
</ul>

<p>The MSB-first approach is slightly more efficient on Z80 because <code class="language-plaintext highlighter-rouge">ADD HL, HL</code> is a single instruction to shift the accumulator, whereas shifting DE left would require two instructions.</p>

<p>Here’s a simple worked example (4-bit: 5 * 3):</p>

<div class="language-plaintext highlighter-rouge"><div class="highlight"><pre class="highlight"><code>  DE=0101 (5), BC=0011 (3), HL=0000

  Iter 1: HL=0000, shift BC -&gt; MSB=0, skip
  Iter 2: HL=0000, shift BC -&gt; MSB=0, skip
  Iter 3: HL=0000, shift BC -&gt; MSB=1, add -&gt; HL=0101
  Iter 4: HL=1010, shift BC -&gt; MSB=1, add -&gt; HL=1111

HL = 1111 = 15
</code></pre></div></div>

<p>Note that this works for signed and unsigned 16 bit numbers, because we’re discarding the top 16 bits of the result.</p>

<h3 id="division">Division</h3>

<p>Next we can look at <code class="language-plaintext highlighter-rouge">udivmod</code> a utility routine that is used as the basis for many later 
division-related word definitions:</p>

<p><img src="/antforth/assets/images/2026-04-04/udivmod.png" alt="udivmod" /></p>

<p>This is unsigned 16-bit division of HL by BC, producing quotient in HL and remainder in DE.</p>

<p>It’s another z80 classic, the  “restoring division algorithm” — the same long division you do by hand, but in binary.</p>

<ol>
  <li>Setup: DE (remainder) = 0, A (bit counter) = 16</li>
  <li>Main loop (one iteration per bit of the dividend):</li>
</ol>

<p>a. Shift the dividend’s MSB into the remainder:
    - <code class="language-plaintext highlighter-rouge">ADD HL, HL</code> — left-shifts HL, pushing the top bit into carry
    - <code class="language-plaintext highlighter-rouge">RL E / RL D</code> — rotates that carry bit into the bottom of DE (the remainder)</p>

<p>This is like “bringing down the next digit” in long division. After 16 iterations, all dividend bits have been shifted out of HL and the quotient bits have been shifted in.</p>

<p>b. Swap registers: <code class="language-plaintext highlighter-rouge">EX DE, HL</code> so HL = remainder, DE = partial quotient. This is needed because <code class="language-plaintext highlighter-rouge">SBC</code> only works on HL.</p>

<p>c. Trial subtraction: <code class="language-plaintext highlighter-rouge">SBC HL, BC</code> — try subtracting the divisor from the remainder. The <code class="language-plaintext highlighter-rouge">OR A</code> first clears carry so <code class="language-plaintext highlighter-rouge">SBC</code> behaves like <code class="language-plaintext highlighter-rouge">SUB</code>.</p>

<p>d. Does the divisor fit?</p>

<ul>
  <li>Yes (no carry): The subtraction is kept. Swap back (<code class="language-plaintext highlighter-rouge">EX DE, HL</code>), then <code class="language-plaintext highlighter-rouge">SET 0, L</code> sets the lowest bit of the quotient to 1</li>
  <li>No (carry set): The divisor was too large. Restore the remainder with <code class="language-plaintext highlighter-rouge">ADD HL, BC</code>` (undoing the subtraction — this is what makes it “restoring” division). Swap back. The quotient bit stays 0</li>
</ul>

<p>e. Loop: Decrement counter, repeat</p>

<ol>
  <li>Finish: After 16 iterations, HL holds the quotient and DE holds the remainder</li>
</ol>

<p>HL serves double duty: it starts as the dividend and ends as the quotient. Each iteration shifts 
one dividend bit out the top (into the remainder) and shifts one quotient bit in at the bottom 
(via <code class="language-plaintext highlighter-rouge">SET 0, L</code>). After 16 iterations, all 16 dividend bits have been consumed and replaced by 16
  quotient bits.</p>

<p>Here’s a simple workedexample (4-bit: 13 / 3)</p>

<p>HL=1101 (13)  DE=0000  BC=0011 (3)</p>

<div class="language-plaintext highlighter-rouge"><div class="highlight"><pre class="highlight"><code>  Iter 1: shift -&gt; DE=0001, try 0001-0011 -&gt; no fit, restore  -&gt; quot bit=0
  Iter 2: shift -&gt; DE=0011, try 0011-0011 -&gt; fits (DE=0000)   -&gt; quot bit=1
  Iter 3: shift -&gt; DE=0001, try 0001-0011 -&gt; no fit, restore  -&gt; quot bit=0
  Iter 4: shift -&gt; DE=0010, try 0010-0011 -&gt; no fit, restore  -&gt; quot bit=0

  HL=0100 (quotient=4), DE=0001 (remainder=1)   13 = 4 * 3 + 1
</code></pre></div></div>

<p>Now we look at <code class="language-plaintext highlighter-rouge">sdivmod</code>, another utility routine:</p>

<p><img src="/antforth/assets/images/2026-04-04/sdivmod.png" alt="sdivmod" /></p>

<p>This is a signed 16-bit division that truncates toward zero (symmetric/C-style semantics), 
where the remainder takes the sign of the dividend.</p>

<p>It’s qute a sneaky routine: it converts both operands to positive, uses <code class="language-plaintext highlighter-rouge">udivmod</code> to do the actual work, 
then fix up the signs of the results afterwards.</p>

<ol>
  <li>Initialize sign flags: A = 0. Bit 0 will track whether to negate the quotient, bit 1 whether to negate the remainder</li>
  <li>Check dividend (HL) sign: If HL is negative (bit 7 of H set), set both bits 0 and 1 in A (OR 3)
This means: a negative dividend means the remainder should be negative (bit 1), and tentatively the
quotient should be negated (bit 0). Then negate HL to make it positive via the 0 - HL two’s
complement pattern</li>
  <li>Check divisor (BC) sign: If BC is negative, toggle bit 0 (XOR 1)
This handles the sign logic: if both operands are negative, the two toggles cancel out and
the quotient stays positive. Only negate BC to make it positive</li>
  <li>Call <code class="language-plaintext highlighter-rouge">udivmod</code>: Now both operands are positive, so unsigned division gives the correct magnitudes for quotient (HL) and remainder (DE)</li>
  <li>Fix remainder sign: If bit 1 is set (dividend was negative), negate DE</li>
  <li>Fix quotient sign: If bit 0 is set (signs differed), negate HL</li>
</ol>

<p>The repeated pattern (<code class="language-plaintext highlighter-rouge">XOR A / SUB L / LD L,A / SBC A,A / SUB H / LD H,A</code>) is a standard Z80
two’s complement negate: it computes <code class="language-plaintext highlighter-rouge">0 - reg_pair</code> using the carry propagation from <code class="language-plaintext highlighter-rouge">SBC A,A</code>
(which produces 0xFF if there was a borrow, 0x00 if not).</p>

<p>The truth table for restoring “signedness” is:</p>

<table>
  <thead>
    <tr>
      <th>Dividend</th>
      <th>Divisor</th>
      <th>Quotient</th>
      <th>Remainder</th>
    </tr>
  </thead>
  <tbody>
    <tr>
      <td>+</td>
      <td>+</td>
      <td>+</td>
      <td>+</td>
    </tr>
  </tbody>
</table>

<p>We close out with the relatively simple definitions for <code class="language-plaintext highlighter-rouge">/</code>, <code class="language-plaintext highlighter-rouge">/MOD</code> and <code class="language-plaintext highlighter-rouge">/MOD</code> - they’re 
simple because they all build on <code class="language-plaintext highlighter-rouge">sdivmod</code>.</p>

<h2 id="logicasm">logic.asm</h2>

<p>This file also has some word definitions of varying complexity. First the simple logical 
operators:</p>

<h3 id="and-or-xor-invert">AND, OR, XOR, INVERT</h3>

<p><img src="/antforth/assets/images/2026-04-04/simple_logic.png" alt="simple logical operators" /></p>

<p>These are essentially 16-bit wrappers around the equivalent 8-bit z80 instructions.</p>

<h3 id="lshift-and-rshift">LSHIFT and RSHIFT</h3>

<p><img src="/antforth/assets/images/2026-04-04/shifts.png" alt="shift operators" /></p>

<p>Again, simple and elegant implementations. Notice the <code class="language-plaintext highlighter-rouge">ADD Hl, HL</code> trick again in <code class="language-plaintext highlighter-rouge">w_LSHIFT</code>.</p>

<h3 id="equals-less-greater-zero_equals-zero_less-u_less">EQUALS, LESS, GREATER, ZERO_EQUALS, ZERO_LESS, U_LESS</h3>

<p>More simple wrapper functions, nothing particularly worth talking about in here.</p>

<h2 id="testing">Testing</h2>

<p>Once again we perform our solemn “Human In The Loop” duty, and scrutinise the test results:</p>

<p><img src="/antforth/assets/images/2026-04-04/tests_1_4.png" alt="running unit tests" /></p>

<p>That’s a lot of tests! The built in test thread is starting to get a bit cumbersome. Notice 
this though:</p>

<p><img src="/antforth/assets/images/2026-04-04/branching_tests.png" alt="branching tests" /></p>

<p>Now that we have some useful relational operators we can combine them with <code class="language-plaintext highlighter-rouge">QBRANCH</code> that we 
glossed over in an earlier post, and check the test results in our fledgling proto-Forth 
directly!</p>]]></content><author><name></name></author><summary type="html"><![CDATA[On to the next BMAD task: 1.4 - arithmetic, logic and relational operators.]]></summary></entry><entry><title type="html">Stack and memory primitives</title><link href="https://blowback.github.io/antforth/2026/04/03/stack-and-memory-primitives.html" rel="alternate" type="text/html" title="Stack and memory primitives" /><published>2026-04-03T00:00:00+00:00</published><updated>2026-04-03T00:00:00+00:00</updated><id>https://blowback.github.io/antforth/2026/04/03/stack-and-memory-primitives</id><content type="html" xml:base="https://blowback.github.io/antforth/2026/04/03/stack-and-memory-primitives.html"><![CDATA[<p>On to the next BMAD task: 1.3 - stack and memory primitives.</p>

<p>In this sprint, we will acquire the parameter stack manipulation primitives 
<code class="language-plaintext highlighter-rouge">DUP</code>, <code class="language-plaintext highlighter-rouge">DROP</code>, <code class="language-plaintext highlighter-rouge">SWAP</code>, <code class="language-plaintext highlighter-rouge">OVER</code>, <code class="language-plaintext highlighter-rouge">ROT</code>, <code class="language-plaintext highlighter-rouge">PICK</code>, <code class="language-plaintext highlighter-rouge">ROLL</code>, and <code class="language-plaintext highlighter-rouge">DEPTH</code>. We’ll also get the 
return stack primitives <code class="language-plaintext highlighter-rouge">&gt;R</code> and <code class="language-plaintext highlighter-rouge">R&gt;</code> - PUSH and POP operations for the 
separate return stack, basically.</p>

<p>We’ll also get <code class="language-plaintext highlighter-rouge">!</code> and <code class="language-plaintext highlighter-rouge">@</code> (16-bit POKE and PEEK) and <code class="language-plaintext highlighter-rouge">C!</code> and <code class="language-plaintext highlighter-rouge">C@</code> (their 
8-bit equivalents).</p>

<p>If some of the memory operators look oddly familiar, you might be remembering 
them from BBC BASIC, which took a lot of inspiration from Forth. Another example 
of this is BBC BASIC’s built-in assembler, more on that later…</p>

<p>Finally we’ll get a few dictionary space allocation primitives: <code class="language-plaintext highlighter-rouge">HERE</code>, <code class="language-plaintext highlighter-rouge">ALLOT</code>, 
<code class="language-plaintext highlighter-rouge">,</code> (COMMA), <code class="language-plaintext highlighter-rouge">C,</code>, <code class="language-plaintext highlighter-rouge">ALIGN</code>, <code class="language-plaintext highlighter-rouge">ALIGNED</code>, <code class="language-plaintext highlighter-rouge">FILL</code> and <code class="language-plaintext highlighter-rouge">MOVE</code>.</p>

<p>Claude will implement tests for each of these, adding them to the current hard-coded 
test thread in antforth.asm.</p>

<p>Let’s begin! We <code class="language-plaintext highlighter-rouge">/bmad-bmm-create-story 1-3</code>, review the story, then
<code class="language-plaintext highlighter-rouge">/bmad-bmm-dev-story 1-3</code>, followed by <code class="language-plaintext highlighter-rouge">/bmad-bmm-code-review 1-3</code>.</p>

<p>Code review throws up this:</p>

<div class="language-plaintext highlighter-rouge"><div class="highlight"><pre class="highlight"><code> Summary: The implementations look correct based on manual code tracing (DUP through 
FILL, plus ROLL for u=0,1,2). The main problem is that 10+ primitives are claimed 
as tested (tasks marked [x]) but have no test threads. The highest risk is 
MOVE and ROLL — both are complex, both are untested.
</code></pre></div></div>

<p>Shirking tests is a vibe-coding classic, particularly with Claude. BMM’s multi-layer approach is a good 
defense against this proclivity though, as demonstrated here. And always run tests manually after every 
sprint. I told Claude to go ahead and automatically fix those issues.</p>

<p>While that was happening, I researched <code class="language-plaintext highlighter-rouge">ROLL</code> a bit further. It’s inclusion here 
(and also <code class="language-plaintext highlighter-rouge">PICK</code>) is a little odd as its not an ANS Forth <em>core</em> word, but rather 
an <em>extension</em>. Furthermore, Forth purists consider these words to be bad practice. 
Neither CamelForth nor JonesForth implement them.</p>

<p>Tracing back through or BMAD docs I see these were included early on in FR17 of our Product Requirements 
Document, so this is a mild <em>human</em> oversight. We don’t technically need them at this stage, 
but it doesn’t <em>hurt</em> to have these implemented now so I’m not going to take any 
further action beyond scrutinizing this most complex primitive and making sure it’s 
well covered by testing later on (when we’ve got a proper interpreter and complicated 
tests are easier to write).</p>

<h2 id="stack_opsasm">stack_ops.asm</h2>

<p>Before we look at the <code class="language-plaintext highlighter-rouge">ROLL</code> implementation, let’s appreciate some of the other stack 
primitives:</p>

<p><img src="/antforth/assets/images/2026-04-03/stackops.png" alt="basic stack operations" /></p>

<p><code class="language-plaintext highlighter-rouge">DUP</code> is beautifully simple: the Top-of-Stack is already in BC, so we just need to <code class="language-plaintext highlighter-rouge">PUSH BC</code> 
to duplicate that onto the top of the z80 stack. Similarly <code class="language-plaintext highlighter-rouge">DROP</code> is a simple <code class="language-plaintext highlighter-rouge">POP BC</code> - 
we remove the top entry in the z80 stack into our top-of-stack register BC, and the 
previous value of BC is discarded.</p>

<p><code class="language-plaintext highlighter-rouge">SWAP</code> is equally elegant: get the not-quite-top-of-stack value in HL with a <code class="language-plaintext highlighter-rouge">POP</code>, push our 
current  top-of-stack in BC onto the z80 stack,  and then move HL into BC.</p>

<p><code class="language-plaintext highlighter-rouge">ROT</code> and <code class="language-plaintext highlighter-rouge">PICK</code> have equally elegant implementations:</p>

<p><img src="/antforth/assets/images/2026-04-03/stackops2.png" alt="more basic stack operations" /></p>

<p>Now let’s look at that implementation for <code class="language-plaintext highlighter-rouge">ROLL</code>:</p>

<p><img src="/antforth/assets/images/2026-04-03/roll.png" alt="roll implementation" /></p>

<p>This looks intimidating, but it’s pretty simple: we use the z80’s awesome <code class="language-plaintext highlighter-rouge">LDDR</code> to 
move a block of memory (the parameter stack) around (think <code class="language-plaintext highlighter-rouge">memmove()</code> if you have a 
C background), and the rest of it is a lot of fiddling around to get the right 
addresses in the right registers.</p>

<p>I’ll explain what’s going on with reference to this diagram:</p>

<p><img src="/antforth/assets/images/2026-04-03/forth_roll.png" alt="roll explainer" /></p>

<h1 id="stack-effects">Stack effects</h1>

<p>Look at the top of <code class="language-plaintext highlighter-rouge">ROLL</code>’s definition and you’ll see this:</p>

<div class="language-plaintext highlighter-rouge"><div class="highlight"><pre class="highlight"><code>; ROLL ( xu xu-1 ... x0 u -- xu-1 ... x0 xu )
</code></pre></div></div>

<p>The bit in parentheses is called the <em>stack effect</em> and it’s a pithy Forth 
commenting convention for showing the state of the stack <em>before</em> we call 
<code class="language-plaintext highlighter-rouge">ROLL</code> (that’s the left hand bit, before the <code class="language-plaintext highlighter-rouge">--</code>) and also the state of 
the stack <em>after</em> the call (that’s the right hand bit, after the <code class="language-plaintext highlighter-rouge">--</code>).</p>

<p>On each side, the rightmost item is the top-of-stack.</p>

<p>So here was can see that <em>on entry</em> <code class="language-plaintext highlighter-rouge">u</code> is on the top of the stack followed byte
u+1 other values that we’re calling <code class="language-plaintext highlighter-rouge">xu</code>, <code class="language-plaintext highlighter-rouge">xu-1</code>, <code class="language-plaintext highlighter-rouge">xu-2</code>, <code class="language-plaintext highlighter-rouge">x0</code> etc. and 
<em>on exit</em> <code class="language-plaintext highlighter-rouge">xu</code> is on the top of the stack, <code class="language-plaintext highlighter-rouge">u</code> is no longer to be seen, 
and everything else has shuffled UP one place into the gap that 
was <code class="language-plaintext highlighter-rouge">xu</code>’s previous location.</p>

<p>So, let’s imagine that we have 4 values <code class="language-plaintext highlighter-rouge">x3, x2, x1, x0</code> on the stack 
plus our value for <code class="language-plaintext highlighter-rouge">u</code> which is 3 – we want to roll the stack so that 
<code class="language-plaintext highlighter-rouge">x3</code> is on top.</p>

<p>The code starts by multiplying <code class="language-plaintext highlighter-rouge">u</code> by 2, because 3 16-bit values is 
the same as 6 8-bit values, and z80 addressing is all byte based. We 
push this count onto the stack, because we’ll need it later when we 
do the bulk memory move operation.</p>

<p>Remember, we’re pushing it onto the z80 machine stack, not the Forth 
parameter stack, where top-of-stack is represented by the BC register, 
so in effect we’re pushing to position 1 of the Forth parameter stack, BC 
stays on top.</p>

<p>Once we have that value we add it to the stack pointer to get the 
address of <code class="language-plaintext highlighter-rouge">x3</code>. But because we just PUSHed something to the stack (the 
byte count) that value is now off by 2, so we subtract 2 from HL to 
compensate. Now HL points to <code class="language-plaintext highlighter-rouge">x3</code>.</p>

<p>Next we load that value into the DE register. Then we <code class="language-plaintext highlighter-rouge">POP BC</code> which takes 
the byte count off the z80 stack and into the BC register, then we PUSH DE 
which means that <code class="language-plaintext highlighter-rouge">x3</code> is now top of the z80 stack (but not top of the Forth 
parameter stack, because BC now has the byte count in it).</p>

<p>Next we set up the source and destination addresses for the bulk memory 
move. The destination (where we’re going to copy byte <strong>to</strong>) is always in 
DE (DEstination) and the source (where we’re going to copy bytes <strong>from</strong>) 
is always in HL. We get HL by adding the byte count to SP, plus 1 because 
we want to start with the second byte of <code class="language-plaintext highlighter-rouge">x2</code>. We copy this value to 
DE and add 2, so DE now points to the <code class="language-plaintext highlighter-rouge">x3</code> on the bottom of the stack.</p>

<p>Our byte count is already in BC, so when we execute the <code class="language-plaintext highlighter-rouge">LDDR</code> instruction,
6 bytes (3 words) get copied from HL to DE.</p>

<p>Once that’s finished the stack is a bit cluttered as we’ve got duplicate 
 <code class="language-plaintext highlighter-rouge">x0</code> entries from all this shuffling. So we <code class="language-plaintext highlighter-rouge">POP HL</code> to <code class="language-plaintext highlighter-rouge">x3</code> from the top of 
the z80 machine stack, and move that HL value into BC so now it’s at the 
top of the Forth parameter stack also.</p>

<p>Finally we add 2 to SP to ‘erase’ the duplicate <code class="language-plaintext highlighter-rouge">x0</code> value. Then it’s a 
bit of housekeeping and the job is finished.</p>

<p>Here’s what <code class="language-plaintext highlighter-rouge">ROLL</code> looks like in GForth:</p>

<p><img src="/antforth/assets/images/2026-04-03/roll_gforth.png" alt="roll in GForth" /></p>

<h2 id="memoryasm">memory.asm</h2>

<p>That’s probably enough in-depth analysis for one post, but if you’re 
feeling studious there are some equally elegant morsels in <code class="language-plaintext highlighter-rouge">memory.asm</code>:</p>

<p><img src="/antforth/assets/images/2026-04-03/memory_ops.png" alt="memory ops" /></p>

<p>These concise efficient machine code words are the secret behind Forth’s 
reputation for performance: effectively, it’s just a fancy way of stringing 
z80 subroutine calls together whilst adding very little overhead.</p>

<h2 id="testing">Testing</h2>

<p>To wrap up, we must remain vigilant on the testing front and run the test 
suite for ourselves.</p>

<p><img src="/antforth/assets/images/2026-04-03/test_1_3.png" alt="running unit tests" /></p>

<p>All looks good: I also checked the code to make sure that I understand what 
the test thread (in antforth.asm) and that it looks reasonable, and isn’t just 
returning “everything’s OK, honest!”</p>]]></content><author><name></name></author><summary type="html"><![CDATA[On to the next BMAD task: 1.3 - stack and memory primitives.]]></summary></entry><entry><title type="html">Inner interpreter</title><link href="https://blowback.github.io/antforth/2026/03/15/inner-interpreter.html" rel="alternate" type="text/html" title="Inner interpreter" /><published>2026-03-15T00:00:00+00:00</published><updated>2026-03-15T00:00:00+00:00</updated><id>https://blowback.github.io/antforth/2026/03/15/inner-interpreter</id><content type="html" xml:base="https://blowback.github.io/antforth/2026/03/15/inner-interpreter.html"><![CDATA[<p>On to the next BMAD task: 1.2 - inner interpreter and threading.</p>

<p>We <code class="language-plaintext highlighter-rouge">/bmad-bmm-create-story 1-2</code> to create the next story that will guide Claude’s 
development. We got story 1-2 for free last time as part of the initial planning 
process, but from here on in we generate stories on-demand just before we need them. 
This helps the overall development workflow adapt to changes in direction caused by 
discoveries and pivots in ongoing development. It’s preferable to trying to nail 
all the stories down in one go at the outset, as not all developmental challenges 
can be anticipated.</p>

<p>This produces a document in <code class="language-plaintext highlighter-rouge">_bmad-output/implementation-artificats/1-2-inner-interpreter-and-threading.md</code> 
which will be Claude’s development plan. We must review the story carefully, and manually 
address any problems. Once we’re happy we can kick off the development with  <code class="language-plaintext highlighter-rouge">/bmad-bmm-dev-story 1-2</code>
and once that is complete we will <code class="language-plaintext highlighter-rouge">/reset/</code> Claude to get a fresh context and start a 
code review with <code class="language-plaintext highlighter-rouge">/bmad-bmm-code-review 1-2</code>.</p>

<p>There are a couple of minor issues which we elect to fix automatically.</p>

<p>Finally, we’ve got an actual working test:</p>

<p><img src="/antforth/assets/images/2026-03-15_01-03.png" alt="First test" /></p>

<p>But how’s that actually doing anything? We don’t have an interpreter yet!</p>

<h2 id="inner_interpreterasm">inner_interpreter.asm</h2>

<p>In Forth, the thing that executes “programs” is called the <em>outer interpreter</em>, and 
while we don’t have that yet we do have a bare bones <em>inner</em> interpreter: this is 
the code that can execute sequences of Forth <em>words</em>.</p>

<p>A Forth <em>word</em> is either z80 machine code that gets executed directly, or it’s 
a very specific bit of machine code called <code class="language-plaintext highlighter-rouge">DOCOL</code> that knows how to execute 
 sequences of other Forth words. So you build Forth words from other Forth 
words, and some of those words might be machine code primitives.</p>

<p>In our barebones inner interpreter, we’ve now got some basic primitives like <code class="language-plaintext highlighter-rouge">LIT</code>:</p>

<p><img src="/antforth/assets/images/2026-04-03/lit.png" alt="LIT" /></p>

<p>Here <code class="language-plaintext highlighter-rouge">w_LIT</code> is the header for the word (the header permits it to be linked 
into the dictionary of all Forth words), and <code class="language-plaintext highlighter-rouge">w_LIT_cf</code> is the actual code 
that gets implemented when <code class="language-plaintext highlighter-rouge">LIT</code> is used (the ‘_cf’ means “code field”).</p>

<p><code class="language-plaintext highlighter-rouge">LIT</code> is what compiles a literal (like the <code class="language-plaintext highlighter-rouge">2</code> in <code class="language-plaintext highlighter-rouge">: DOUBLE 2 * </code>) into a 
word. It does this by taking the next argument after the <code class="language-plaintext highlighter-rouge">LIT</code>, let’s say 
<code class="language-plaintext highlighter-rouge">2</code>, and sticking that on the top of the parameter stack, adjusting IP 
to move past the <code class="language-plaintext highlighter-rouge">2</code>.</p>

<p>The code might look a little weird, but this is because we are using an 
optimisation and keeping our <code class="language-plaintext highlighter-rouge">TOS</code> (<em>Top of Stack</em>) in register BC, while 
the z80 stack holds the rest of the parameter stack. So that first 
<code class="language-plaintext highlighter-rouge">PUSH BC</code> moves TOS onto the z80 stack, and then we fetch a new value into 
BC, or into the new Top-of-Stack in other words.</p>

<p>We also have this definition for <code class="language-plaintext highlighter-rouge">EXECUTE</code>:</p>

<p><img src="/antforth/assets/images/2026-04-03/execute.png" alt="EXECUTE" /></p>

<p>This takes an “xt” (eXecution Token - a pointer to a function) from the 
parameter stack and executes it. We now know that the top of our parameter 
stack is in BC, so all we need to do is move BC into HL so that we can 
jump to it.</p>

<p>There are a couple of other new definitions (<code class="language-plaintext highlighter-rouge">BRANCH</code>, <code class="language-plaintext highlighter-rouge">?BRANCH</code>) which 
we’ll gloss over for now: they implement unconditional and conditional 
branching respectively.</p>

<h2 id="ioasm">io.asm</h2>

<p>We now also have the rather exciting primitive <code class="language-plaintext highlighter-rouge">EMIT</code>:</p>

<p><img src="/antforth/assets/images/2026-04-03/emit.png" alt="EMIT" /></p>

<p>Which takes a byte from the TOS (BC register again) and uses CP/M’s 
BDOS to output it to the terminal. This routine is mostly register 
juggling to get the character we want to emit in the E register and 
the value <code class="language-plaintext highlighter-rouge">C_WRITE</code> into the C register before we call the BDOS.</p>

<p>Note that the call to BDOS is wrapped with <code class="language-plaintext highlighter-rouge">BDOS_SAVE</code> and <code class="language-plaintext highlighter-rouge">BDOS_RESTORE</code> 
to protect our registers from any corruption by BDOS.</p>

<h2 id="antforthasm">antforth.asm</h2>

<p>Now we can see how the test is actually implemented:</p>

<p><img src="/antforth/assets/images/2026-04-03/test1.png" alt="test 1" /></p>

<p>Here we’re hardcoding a <code class="language-plaintext highlighter-rouge">thread</code> which is basically <code class="language-plaintext highlighter-rouge">LIT 'A' EMIT</code> and 
then executing it.</p>

<p>We’ve also got a test of <code class="language-plaintext highlighter-rouge">DOCOL</code>:</p>

<p><img src="/antforth/assets/images/2026-04-03/test2.png" alt="test 2" /></p>

<p>whose definition looks like:</p>

<p><img src="/antforth/assets/images/2026-04-03/test_docol.png" alt="test DOCOL" /></p>

<p>Which is basically the same as test 1, but this time wrapped in a 
<code class="language-plaintext highlighter-rouge">DOCOL</code> - i.e. it’s a simulation of a Forth word definition.</p>

<p>There are a few more tests to do with branching, and then one last 
one to test <code class="language-plaintext highlighter-rouge">EXECUTE</code>:</p>

<p><img src="/antforth/assets/images/2026-04-03/test6.png" alt="test 6" /></p>

<p>Rather than just call <code class="language-plaintext highlighter-rouge">BYE</code> directly, we’re taking its address, 
pushing that onto the parameter stack, and then using <code class="language-plaintext highlighter-rouge">EXECUTE</code> to 
call it.</p>]]></content><author><name></name></author><summary type="html"><![CDATA[On to the next BMAD task: 1.2 - inner interpreter and threading.]]></summary></entry><entry><title type="html">Let’s see what we got…</title><link href="https://blowback.github.io/antforth/2026/03/14/lets-see-what-we-got.html" rel="alternate" type="text/html" title="Let’s see what we got…" /><published>2026-03-14T00:00:00+00:00</published><updated>2026-03-14T00:00:00+00:00</updated><id>https://blowback.github.io/antforth/2026/03/14/lets-see-what-we-got</id><content type="html" xml:base="https://blowback.github.io/antforth/2026/03/14/lets-see-what-we-got.html"><![CDATA[<p>Claude has made a basic project scaffold for us, but it’s mostly stub files at this point. What we do have though, is some interesting
stuff going on in <code class="language-plaintext highlighter-rouge">macros.asm</code> and <code class="language-plaintext highlighter-rouge">inner_interpreter.asm</code>.</p>

<p>Our register allocation is:</p>

<ul>
  <li><code class="language-plaintext highlighter-rouge">HL</code> = W (working register — points to the current word’s CFA)</li>
  <li><code class="language-plaintext highlighter-rouge">DE</code> = IP (instruction pointer — points to the next execution token)</li>
  <li><code class="language-plaintext highlighter-rouge">SP</code> = PSP (parameter/data stack pointer)</li>
  <li><code class="language-plaintext highlighter-rouge">IX</code> = RSP (return stack pointer)</li>
  <li><code class="language-plaintext highlighter-rouge">IY</code> = UP (user/task pointer)</li>
  <li><code class="language-plaintext highlighter-rouge">BC</code> = TOS (top-of-stack cached in register)</li>
</ul>

<p>In direct-threaded code (DTC), a word’s code field contains the actual machine code address to jump to. For a compiled (colon) word,
this is the address of <code class="language-plaintext highlighter-rouge">ENTER</code> (also called <code class="language-plaintext highlighter-rouge">DOCOL</code>). For a primitive, the code field is the primitive’s machine code inline.</p>

<p>The three critical routines are <code class="language-plaintext highlighter-rouge">NEXT</code>, <code class="language-plaintext highlighter-rouge">ENTER</code>, and <code class="language-plaintext highlighter-rouge">EXIT</code>.</p>

<p><img src="/antforth/assets/images/2026-03-14_23-49.png" alt="Critical routines" /></p>

<p>In <code class="language-plaintext highlighter-rouge">macro.asm</code> we have the definition of a crucial macro <code class="language-plaintext highlighter-rouge">NEXT</code>, which is what
drives the inner interpreter, moving the execution from one Forth word to the next. <code class="language-plaintext highlighter-rouge">NEXT</code>` is the macro/routine 
executed at the end of every primitive. It fetches the next execution token (a CFA address) from the thread, 
loads it into W (HL), and jumps through it.</p>

<p>Here’s our implementation:</p>

<div class="language-plaintext highlighter-rouge"><div class="highlight"><pre class="highlight"><code>; === NEXT — Inner interpreter fetch-decode-execute ===
; DE = IP, fetch [DE] into HL, advance DE by 2, JP (HL)
    MACRO NEXT
        EX      DE, HL          ; HL = IP
        LD      E, (HL)
        INC     HL
        LD      D, (HL)
        INC     HL
        EX      DE, HL          ; DE = IP+2, HL = code field address
        JP      (HL)
    ENDM
</code></pre></div></div>

<p>We can compare this with “Case Study 3: Z80” in <a href="https://www.bradrodriguez.com/papers/moving2.htm">Moving Forth: part 2</a> - since this
is where we swiped our register allocation scheme from. Brad Rodriguez gives two slightly different implementations of <code class="language-plaintext highlighter-rouge">NEXT</code> for a 
direct-threaded interpreter such as ours:</p>

<div class="language-plaintext highlighter-rouge"><div class="highlight"><pre class="highlight"><code>DTC-NEXT: LD A,(DE) (7) (IP)-&gt;W, increment IP
          LD L,A    (4)
          INC DE    (6)
          LD A,(DE) (7)
          LD H,A    (4)
          INC DE    (6)
          JP (HL)   (4) jump to address in W
</code></pre></div></div>

<p>alternate version (same number of clock cycles):</p>

<div class="language-plaintext highlighter-rouge"><div class="highlight"><pre class="highlight"><code>
DTC-NEXT: EX DE,HL  (4) (IP)-&gt;W, increment IP
NEXT-HL:  LD E,(HL) (7)
          INC HL    (6)
          LD D,(HL) (7)
          INC HL    (6)
          EX DE,HL  (4)
          JP (HL)   (4) jump to address in W
</code></pre></div></div>

<p>We’re loading the next “word” to be executed from where our “instruction pointer” (IP) is pointing, and we’re executing it (by JumPing to it).
The HL register does double duty here: it’s the most flexible z80 register, and here we’re using its ability to do memory load indirection <em>and</em> 
an indirect jump. Hence all the <code class="language-plaintext highlighter-rouge">EX</code>ing so we’re using HL in the right place at the right time.</p>

<p>In direct-threaded code the CFA <em>contains</em> either machine code (for primitives) or a <code class="language-plaintext highlighter-rouge">JP DOCOL</code> instruction (for colon words). Jumping through W means primitives run their own body directly.</p>

<p>The rest of <code class="language-plaintext highlighter-rouge">macro.asm</code> contains some interesting looking chicanery using sjasmplus’ Lua interpreter. This is to allow us to use macros to 
define Forth words whilst using the fancy hash-based dictionary algorithm that we’ve chosen. It all looks terribly exciting, but I wouldn’t 
be surprised if this is a source of friction later.</p>

<p>In <code class="language-plaintext highlighter-rouge">inner_interpreter.asm</code> is our definition of <code class="language-plaintext highlighter-rouge">DOCOL</code>:</p>

<div class="language-plaintext highlighter-rouge"><div class="highlight"><pre class="highlight"><code>; === DOCOL — Enter colon definition ===
; Push IP (DE) onto return stack (IX), set IP to body (following JP DOCOL)
DOCOL:
        ; Push current IP onto return stack
        DEC     IX
        DEC     IX
        LD      (IX+0), E
        LD      (IX+1), D
        ; W (HL) points to code field (the JP DOCOL), body = HL+3
        INC     HL
        INC     HL
        INC     HL
        EX      DE, HL          ; DE = IP = body address
        NEXT
</code></pre></div></div>
<p>We can compare this with the final DTC word <code class="language-plaintext highlighter-rouge">ENTER</code> in Moving Forth - part2. Straight away we can see we’ve got an issue: we <code class="language-plaintext highlighter-rouge">EX DE,HL</code> and then
call <code class="language-plaintext highlighter-rouge">NEXT</code>, and the first thing <code class="language-plaintext highlighter-rouge">NEXT</code> does is <code class="language-plaintext highlighter-rouge">EX DE, HL</code> so we’ve got a NOP. This is what Brad is hinting with his <code class="language-plaintext highlighter-rouge">NEXTHL</code> alternate 
entrypoint, and we can ask Claude to make that change too.</p>

<p>First <code class="language-plaintext highlighter-rouge">/bmad-agent-bmm-dev</code> to start up a generic dev agent, then we ask him:</p>

<div class="language-plaintext highlighter-rouge"><div class="highlight"><pre class="highlight"><code>❯ in src/inner_interpreter.asm we have a DOCOL macro, this ends with `EX DE, HL: NEXT` and the first line of NEXT (defined in macros.asm) is another `EX DE, HL`. We should have another macro NEXTHL which is the last instructions of NEXT. Then words like DOCOL can skip the `EX DE, HL` and call HLNEXT directly. NEXT can also use NEXTHL.

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

<p>Then <code class="language-plaintext highlighter-rouge">/bmad-bmm-code-review</code> and ask it to review the recent changes. Et voila:</p>

<div class="language-plaintext highlighter-rouge"><div class="highlight"><pre class="highlight"><code>Verdict: Changes are correct and beneficial. The NEXTHL optimization is sound — saves 8 T-states per DOCOL invocation with no behavioral change. No bugs found. Ready to commit.

</code></pre></div></div>]]></content><author><name></name></author><summary type="html"><![CDATA[Claude has made a basic project scaffold for us, but it’s mostly stub files at this point. What we do have though, is some interesting stuff going on in macros.asm and inner_interpreter.asm.]]></summary></entry><entry><title type="html">Friday 13th</title><link href="https://blowback.github.io/antforth/2026/03/13/friday-13th.html" rel="alternate" type="text/html" title="Friday 13th" /><published>2026-03-13T00:00:00+00:00</published><updated>2026-03-13T00:00:00+00:00</updated><id>https://blowback.github.io/antforth/2026/03/13/friday-13th</id><content type="html" xml:base="https://blowback.github.io/antforth/2026/03/13/friday-13th.html"><![CDATA[<p>Friday the 13th seems as good a time as any to start off a project! Let’s begin by reviewing the story 
file that was created last time at <code class="language-plaintext highlighter-rouge">_bmad-output/implementation-artifacts/1-1-project-scaffolding-and-build-toolchain.md</code>.</p>

<p><em>Always review these files carefully!</em>  - the devil is in the details. When you’re happy you can change the status at the 
top of the file to “ready-for-dev” or some words like that (in previous versions of BMAD development was locked out until this 
happened, but now I believe it’s not strictly necessary - still  a good habit to have though, I think).</p>

<p>You can see that this task is largely to do with scaffolding out the project: installing toolchains, setting up git, 
setting up directory structure, and stubbing out some files. Exactly the kind of donkey work that an LLM excels at.</p>

<p>To start the development process I used <code class="language-plaintext highlighter-rouge">/bmad-bmm-dev-story 1-1</code>. Technically you don’t need the 1-1 and the agent 
will figure out the next active query itself. I used to give it the full <code class="language-plaintext highlighter-rouge">@_bmad-output/implementation-artifacts/1-1-project-scaffolding-and-build-toolchain.md</code> in 
the belief that I was saving some precious context by pointing directly at the right instructions, but I’ve grown lazy
and settled on just giving <code class="language-plaintext highlighter-rouge">1-1</code> as a compromise.</p>

<p>Development for this task takes a couple of minutes:</p>

<div class="language-plaintext highlighter-rouge"><div class="highlight"><pre class="highlight"><code> Step 10: Completion

  Ant, story 1.1 — Project Scaffolding &amp; Build Toolchain is complete and ready for review.

  Key accomplishments:
  - Full project structure created with all directories and 22 source files
  - Makefile with asm, disk, test, and clean targets plus incremental build support
  - constants.asm — all memory layout equates and system constants
  - macros.asm — NEXT, DOCOL, EXIT, DEFCODE/DEFWORD/DEFIMMED (with LUA-based XOR-rotate hash), BDOS_SAVE/BDOS_RESTORE
  - structures.asm — DictEntry and UserArea STRUCT definitions
  - antforth.asm — main manifest with ORG 0x0100 and minimal cold start (BYE via BDOS function 0)
  - All 15 architecture-specified stub files created
  - Verified: make builds successfully, binary runs under iz-cpm with clean exit (code 0), incremental builds work

  Story file: _bmad-output/implementation-artifacts/1-1-project-scaffolding-and-build-toolchain.md (status: review)

  Notes:
  - iz-cpm was installed from GitHub releases (not available via cargo install as the story notes suggested)
  - Used sj.insert_label instead of sj.add_label for sjasmplus v1.21.0 LUA compatibility
  - Used --raw CLI flag instead of SAVEBIN directive (which requires DEVICE emulation mode)

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

<p>The agent is complaining about some features not being available 
in the “latest” version of sjasmplus, and straight away I see I missed an important detail in the story file: it specifies 
version 1.20.0, whereas the latest release is 1.20.1. I just called the agent out directly:</p>

<div class="language-plaintext highlighter-rouge"><div class="highlight"><pre class="highlight"><code>❯ why are we using sjasmplus 1.21.0 ? latest is 1.21.1

● It was pre-installed on the system at v1.21.0. Let me check if v1.21.1 adds sj.add_label (which would simplify the LUA workaround) and upgrade it.
</code></pre></div></div>

<p>and it continues with:</p>

<div class="language-plaintext highlighter-rouge"><div class="highlight"><pre class="highlight"><code>● Still no sj.add_label — sj.insert_label is the correct API. Our code is already using that. Let me just verify the build still works with v1.22.0:

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

<p>so that whole thing turns out to have been a hallucination! I don’t like the look of that <code class="language-plaintext highlighter-rouge">iz-cpm</code> grumble either, and the 
whole <code class="language-plaintext highlighter-rouge">SAVEBIN</code> thing is a bit of a worry. Roundabout this time it occured to me that installing (updating, at least) system 
binaries is not very 2026, so I asked it to switch to a docker build container:</p>

<div class="language-plaintext highlighter-rouge"><div class="highlight"><pre class="highlight"><code> you know what, i think i'd be happier building a docker container that has the correct versions of sjasmplus, iz-cpm and cpmtools in it, rather than relying on system
binaries. this will make using github actions easier later too if we want to.

● Good idea. Let me check what's needed to build each tool, then create the Dockerfile.

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

<p>and a 1m31s later:</p>

<div class="language-plaintext highlighter-rouge"><div class="highlight"><pre class="highlight"><code>● Done. The Docker toolchain image (antforth-toolchain) contains:
  - sjasmplus built from main (v1.22.0+)
  - iz-cpm v1.3.4
  - cpmtools (from Debian packages)

  Usage:
  - make docker-build — build the image
  - make docker — assemble via Docker
  - make docker-test — build + run under iz-cpm
  - make docker-disk — build CP/M disk image

  The multi-stage build keeps the runtime image small (no compiler toolchain in the final image). The .dockerignore excludes build artifacts and non-source directories from the
  context.

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

<p>It has built a container image that we can use to portably build antforth in less time than it’s taken me to write about it.</p>

<p>One important note about changing track like this: it directly contradicts the specific instructions in the story file 
and in the architecture document. What we’re doing is called a “course correction”, and there’s (of course) a BMAD command 
to deal with that (more than one actually, depending on the complexity of the correction). But for a minor change like this,
at such an early stage, it’s easier to ask the dev agent to make the change, and then ask it to update the documentation to match:</p>

<div class="language-plaintext highlighter-rouge"><div class="highlight"><pre class="highlight"><code>❯ can you update the story and maybe the architecture document to reflect that we've elected to use a docker build container?

● Done. Updated both documents:

  Architecture doc — added Docker to the toolchain table with a description of the multi-stage build approach, added Dockerfile/.dockerignore to the directory structure, and
  expanded the build process section with the docker targets.

  Story file — added Dockerfile and .dockerignore to the file list, added Docker completion notes, and added a change log entry.

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

<p>If you skip this step, the <em>next</em> step (which is an adversarial code review) WILL flag up the differences, and worst case may 
even revert them, which can be quite heartbreaking. <em>Adversarial</em> means that the review agent MUST find something to complain 
about, so a spec deviation like that is an easy out for it - we want it to work harder and find actual issues.</p>

<p>Speaking of which, let us clear the context (I use <code class="language-plaintext highlighter-rouge">/reset</code> - I used to use <code class="language-plaintext highlighter-rouge">/clear</code> but for some reason recently Claude code
decided that <code class="language-plaintext highlighter-rouge">/clear</code> is an alias of <code class="language-plaintext highlighter-rouge">/compact</code> - which is the <em>exact opposite</em> of what I want!)</p>

<p>Now we can run the code review with <code class="language-plaintext highlighter-rouge">/bmad-bmm-code-review 1-1</code> (I’m using my usual technique of typing <code class="language-plaintext highlighter-rouge">review</code> and picking from 
one of the suggestions).</p>

<p>The review agent (actually a dev agent with his code-review hat on) finds some issues sure enough:</p>

<div class="language-plaintext highlighter-rouge"><div class="highlight"><pre class="highlight"><code>---
  Step 3: Adversarial Review

  ---
  🔥 CODE REVIEW FINDINGS, Ant!

  Story: 1-1-project-scaffolding-and-build-toolchain.md
  Git vs Story Discrepancies: 2 found
  Issues Found: 2 High, 4 Medium, 3 Low

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

<p>The “High” issues are to do with macros being in the wrong place. Amusinly one of the medium issues is:</p>

<div class="language-plaintext highlighter-rouge"><div class="highlight"><pre class="highlight"><code>M3: architecture.md modified but not documented in story File List
  - Git shows architecture.md as modified (unstaged)
  - Story Change Log correctly notes "Updated architecture document to reflect containerised build approach"
  - But the story's File List does NOT include architecture.md
  - Impact: Incomplete documentation of changes
</code></pre></div></div>

<p>so we didn’t entirely get away with our course correction!</p>

<p>Luckily there’s nothing too earth-shattering so I used the <code class="language-plaintext highlighter-rouge">1. Fix them immediately</code> option. You can also write new 
tasks and subtasks for the necessary corrections into the story file, and go back to a fresh dev agent and start 
again. This  is a good idea if you’re running out of context. If things go really south, you can convene an emergency 
retro with your entire “team” and ask them “what the hell happened?”. I will leave this for a later post…it’s bound 
to crop up.</p>

<p>This took a while, as further isses were encountered and remedied, but eventually:</p>

<div class="language-plaintext highlighter-rouge"><div class="highlight"><pre class="highlight"><code> Step 5: Review Complete!

  All HIGH and MEDIUM issues have been fixed and verified:

  ┌──────────────────────────────────┬────────────────────────────────────────────────────────────────────────────────────────────────────────┬───────────────────────────────┐
  │              Issue               │                                                  Fix                                                   │           Verified            │
  ├──────────────────────────────────┼────────────────────────────────────────────────────────────────────────────────────────────────────────┼───────────────────────────────┤
  │ H1 DOCOL/EXIT misplaced          │ Moved from macros.asm to inner_interpreter.asm                                                         │ Build passes                  │
  ├──────────────────────────────────┼────────────────────────────────────────────────────────────────────────────────────────────────────────┼───────────────────────────────┤
  │ H2 Macros had 3 latent bugs      │ Fixed bit32→native Lua 5.5 ops, LEN→LUA name length via DEFINE/sj.get_define, added DEFCODE "BYE" to   │ Build passes, BYE entry in    │
  │                                  │ exercise macros                                                                                        │ binary                        │
  ├──────────────────────────────────┼────────────────────────────────────────────────────────────────────────────────────────────────────────┼───────────────────────────────┤
  │ M1 Include order wrong           │ Reordered antforth.asm to match architecture spec                                                      │ Build passes                  │
  ├──────────────────────────────────┼────────────────────────────────────────────────────────────────────────────────────────────────────────┼───────────────────────────────┤
  │ M2 disk/ won't survive clone     │ Added disk/.gitkeep                                                                                    │ File exists                   │
  ├──────────────────────────────────┼────────────────────────────────────────────────────────────────────────────────────────────────────────┼───────────────────────────────┤
  │ M3 architecture.md not in File   │ Added to story File List                                                                               │ Story updated                 │
  │ List                             │                                                                                                        │                               │
  ├──────────────────────────────────┼────────────────────────────────────────────────────────────────────────────────────────────────────────┼───────────────────────────────┤
  │ M4 Cold start layout fragile     │ Resolved by H1 + M1 fixes                                                                              │ Binary layout clean           │
  └──────────────────────────────────┴────────────────────────────────────────────────────────────────────────────────────────────────────────┴───────────────────────────────┘

  Story Status: done
  Issues Fixed: 6 (2 HIGH, 4 MEDIUM)
  Action Items Created: 0

  Sprint status synced: 1-1-project-scaffolding-and-build-toolchain → done

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

<p>The review step can be long and arduous, it often takes more time than the development process. It is tempting sometimes
to skip it, but I try to never, ever skip this crucial step. It is time well spent.</p>

<h2 id="results">Results</h2>

<p>At this point we have a directory structure that looks like this:</p>

<p><img src="/antforth/assets/images/2026-03-13_23-49.png" alt="Initial directory structure" /></p>

<p>Seems like as good a time as any to commit. Incidentally, I have strict instructions in various Claude config files for him 
to keep his hands off the repo. It’s a human-controlled firewall as far as I’m concerned, a bit like Zion in the Matrix movies. 
Also the “Co-authored by Claude/Anthropic” advert it inserts really greats my gears given the $$$ I’m forking over every month.</p>

<p>Not much else we can do at this stage, except maybe admire our Makefile and take some of the make targets for a spin, including 
the Docker build container that we pivoted to.</p>

<p>We can even <code class="language-plaintext highlighter-rouge">make test</code> as we have a rudimentary test executed via <code class="language-plaintext highlighter-rouge">iz-cpm</code>. Doesn’t do much yet, but it’s a promising start.</p>

<p>An aside on testing: Claude Code is superb at writing tests, but it is extremely lax at maintaining 100% pass rate. You will
quickly see weasel words like “some tests are failing, but it’s not code that I just changed so that can’t have been me…”.
One of the main jobs as the “human-in-the-loop” is to stamp that shit out as soon as it occurs. Never, ever let a dev/review 
cycle end with failing tests, broken tests, skipped tests, or – if you-re feeling particularly puritanical – warnings.</p>

<p>In my experience, you can add this to all the Claude config files you can find, and to the PRD, architecture docs and even 
individual story files - Claude will still try to wriggle out of it. Don’t let him!</p>]]></content><author><name></name></author><summary type="html"><![CDATA[Friday the 13th seems as good a time as any to start off a project! Let’s begin by reviewing the story file that was created last time at _bmad-output/implementation-artifacts/1-1-project-scaffolding-and-build-toolchain.md.]]></summary></entry><entry><title type="html">If you’ve seen one Forth…</title><link href="https://blowback.github.io/antforth/2026/03/12/if-youve-seen-one-forth.html" rel="alternate" type="text/html" title="If you’ve seen one Forth…" /><published>2026-03-12T00:00:00+00:00</published><updated>2026-03-12T00:00:00+00:00</updated><id>https://blowback.github.io/antforth/2026/03/12/if-youve-seen-one-forth</id><content type="html" xml:base="https://blowback.github.io/antforth/2026/03/12/if-youve-seen-one-forth.html"><![CDATA[<p>“If you’ve seen one Forth…you’ve seen one Forth” the old joke goes. There’s something about the Forth language
that makes you want to write your <em>own</em> Forth language. Possibly because it attracts the sort of noble pioneers 
who relish the challenge of carving out new frontiers from the unyielding bedrock of pure machine code, or possibly 
because it attracts the sorts of people who are easily distracted…</p>

<p>A few years ago, whilst learning Rust, I completed (well, ok, 90%) the  excellent <a href="https://exercism.org/tracks/rust">Exercism.org Rust track</a>.
One of the exercises was a sort of proto-forth interpreter, so that got me thinking. More recently, I bought a 
<a href="https://feersumbeasts.com/microbeast.html">Feersum Technology MicroBeast</a> - a very capable Z80-based retro-computer 
with an 8MHz CPU, 512K of RAM and 512K of Flash. This is enormous fun to write code for, and my efforts so far have 
involved using modern tooling on my desktop and copying assembled binaries over. But the options for developing 
<em>on device</em> are somewhat limited. There’s Microsoft Basic, which devolves into a tangle of <code class="language-plaintext highlighter-rouge">GOSUB</code>s before you know it, 
and BBC BASIC also runs on the machine, but with a few issues that make it largely unusable. Various other ancient 
compilers are available if you want them, can find them, and can make them work.</p>

<p>This is exactly the sort of environment Forth is designed for: bootstrapping your way from nothing to a working REPL
in a relatively small amount of code. So I did the obvious thing, and bought every ancient Forth book I could get my 
hands on. There are also some excellent online resources, most notably <a href="https://github.com/nornagon/jonesforth/blob/master/jonesforth.S">jonesforth</a> 
and <a href="https://www.bradrodriguez.com/papers/moving1.htm">Moving Forth</a> - these are monumental resources, worthy of deep 
study.</p>

<p>Moving Forth essentially culminates in <a href="https://www.camelforth.com/news.php">CamelForth</a>, a modern, ANS-compatible Forth 
that can run on a Z80 and that, honestly I could probably have ported in a couple of evenings.</p>

<p>But where’s the fun in that!</p>

<h2 id="ai-assisted-development">AI assisted development</h2>

<p>There’s another reason. It’s a divisive one, so bear with me. Like it or not, AI tools are, for the time being, unavoidable.
I was a sceptic for the longest time, but some early experimentation with Gemini, Kimi K2, and GLM convinced me that vibe coding
was genuinely useful: <em>but only up to a point</em>. In surprisingly little time, for all but the smallest projects, you’re left with
an unmaintainable mess, your LLM burning tokens as it oscillates between two complimentary states, neither of which work for 
different reasons.</p>

<p>Prompt engineering can get you a little further along, but you spend as much time writing prompts to write code as you would have
done writing the code in the first place.</p>

<p>But there is another way! A chance YouTube video led me to a frame work (SuperClaude, iirc) for a more organised approach, and from there 
I migrated to AgentOS, and then finally to <a href="https://github.com/bmad-code-org/BMAD-METHOD">The BMAD Method</a>. Essentially, the premise is,
you use old-fashioned structured, engineering methodologies to break work into small chunks that allow your chosen LLM to use
its context effectively, and avoid going off into the weeds. But the beauty is, you use the LLM itself to do all the tedious business
of gathering user requirements, writing briefs, writing project requirements documents, writing an architecture plan, making a sprint plan,
managing epics and stories, writing a test plan, running unit tests, integration tests and regression tests, etc etc.</p>

<p>In short, you get all the rigour, without any of the ball-ache.</p>

<p>I have used this approach in many personal and professional projects, with great success. I have gone from being a John Henry-esque LLM-refusenik 
to a slightly rabid born-again evangelist.</p>

<h2 id="how-is-that-fun">How is that fun?</h2>

<p><em>Know thyself</em> says the Temple of Apollo at Delphi. I am aware that prior to the dawn of LLMs the chances of me completing any such 
hare-brained projects were virtually nil. Generally you get bogged down in minutae like writing number conversion routines 
or fast division routines that gets in the way of the fun stuff. Inevitably you are distracted by another idea, and break ground on 
yet another new project, and the cycle continues!</p>

<p>But a capable LLM (Claude in my case) can write all the tedious stuff for you: you can pick and choose the fun bits that you want 
to write yourself. Even the stuff you don’t write can teach you a lot, because you’ll be debugging it sooner or later.</p>

<h2 id="why-are-you-blogging-about-it">Why are you blogging about it?</h2>

<p>I’ve demo-d this approach to a couple of colleagues, with varying degrees of success. It isn’t a quick process, and being the human-in-the-loop 
can consume many hours of precious development time. So I intend this blog to be a resource that BMAD neophytes can use to learn at their own 
pace, assuming they have a passing interest in ancient computing languages and “tired iron”. You’ll get to see all the steps I took, warts 
and all.</p>

<h2 id="first-steps">First steps</h2>

<p>In the <code class="language-plaintext highlighter-rouge">docs</code> folder is an initial chat that I had with claude.ai, where we kicked some ideas around and I laid down a couple of 
key concepts that I’d swiped from Moving Forth (direct threading, z80 register allocations). The hash table dictionary idea isn’t novel 
either, I think I read that in one of the eForth books.</p>

<p>Next I created  a new directory, <code class="language-plaintext highlighter-rouge">git init</code>d it, and installed BMAD with <code class="language-plaintext highlighter-rouge">npx bmad-method install</code>.</p>

<p>I copied the text of the chat, and used the command <code class="language-plaintext highlighter-rouge">/bmad-create-product-brief</code> to turn these vague noodlings into a coherent product brief. 
In fact I don’t take the trouble to remember any bmad commands, I usually type a <code class="language-plaintext highlighter-rouge">/</code> and roughly what I’m after, and then pick from the 
auto-complete suggestions, so here I probably typed <code class="language-plaintext highlighter-rouge">/create</code> picked <code class="language-plaintext highlighter-rouge">/bmad-create-product-brief</code> from the suggestions, and then added something 
like <code class="language-plaintext highlighter-rouge">create the brief from the conversation I had with claude in @docs/blah.md</code>.</p>

<p>After answering a lot of questions, you get a brief document (<code class="language-plaintext highlighter-rouge">_bmad-output/planning-artifacts/product-brief-antforth-2026-03-11.md</code>).
As you can see, this is starting to look quite pro already.</p>

<p>The next step is <code class="language-plaintext highlighter-rouge">/bmad-bmm-create-prd</code> which will get a much more detailed document: a Product Requirements Document (or Project Requirements 
Document, if you prefer). Again, you can see it in <code class="language-plaintext highlighter-rouge">_bmad-output/planning-artifacts/prd.md</code>. It can get a bit frothy and biz-devy, but it’s 
worth sticking with it.</p>

<p>The next step is <code class="language-plaintext highlighter-rouge">/bmad-bmm-create-architecture</code> which is a lot more fun if you’re technical as you get to talk about compilers and assemblers 
and stuff. This too nets you a new specification: <code class="language-plaintext highlighter-rouge">_bmad-output/planning-artifacts/architecture.md</code>.</p>

<p>All of these documents form a knowledge-base that will help inform future development. They’re not all included verbatim in each and every 
development prompt, because that would eat your precious context space and leave little room for the actual development. Instead, as part of 
the development cycle, a “planning” agent will pick and choose only the relevant parts of these documents to write an immediate plan for a 
“development” agent who will do the actual work.</p>

<p>At any stage in the processes I have described so far, you have the option of “advanced solicitation” (<em>even more</em> questions for you to answer), 
or “party mode”, which is often <em>hilarious</em>. Essentially you get multiple copies of your LLM role-playing developer, architect, QA, project 
manager etc and they bicker about whatever the current topic is. If you are unsure about the path you’re taking, this can be hugely useful!
Frequently you can also <em>yolo the rest</em> which can be a time-saver if you know what you’re doing, but for your first couple of projects I’d 
recommend going through the process yourself.</p>

<p>At this point I usually like to run <code class="language-plaintext highlighter-rouge">/bmad-bmm-check-implementation-readiness</code> which will catch any errors or omissions in the documents 
we’ve generated so far.</p>

<p>Next it’s time to setup the planning with <code class="language-plaintext highlighter-rouge">/bmad-bmm-sprint-planning</code> then <code class="language-plaintext highlighter-rouge">/bmad-bmm-create-epics-and-stories</code>. These take a while (again) 
and generate a lot of output that you are expected to read, and you <em>should</em> read it. The future quality of your project hinges on you 
detecting errors or discrepancies during these initial stages.</p>

<p>Now we have an epics document (<code class="language-plaintext highlighter-rouge">_bmad-output/planning-artifacts/epics.md</code>) which gives us a high-level overview of the work to be done 
(epics are “big stories”). We also have a plan in <code class="language-plaintext highlighter-rouge">_bmad-output/implementation-artifacts/sprint-status.yaml</code> which we can use to gauge 
development progress. Typically you refer to this file often. Finally, we have the first story in <code class="language-plaintext highlighter-rouge">_bmad-output/implementation-artifacts/1-1-project-scaffolding-and-build-toolchain.md</code> and it’s this file that will guide the first development phase. It is always worth checking these <em>scrupulously</em> and pushing 
back hard on anything you’re unhappy with, but we’ll leave that and the rest of the dev cycle for another time.</p>

<h2 id="it-made-sense-when-you-said-it-but">It made sense when you said it, but…</h2>

<p>If you’re encouraged to try this yourself and it’s all a bit baffling, don’t worry. In the latest BMAD (v6) it is very difficult to go wrong. 
If you try to execute a step out of order, agents will gently guide you back on track. If you don’t know what’s next, just ask in simple, 
natural language.</p>

<p>One of the most important things to realise is that the built-in help <code class="language-plaintext highlighter-rouge">/bmad-help i don't understand blah blah blah</code> is <em>fantastic</em>  - if 
you’re in doubt, just ask it.</p>

<p>The Discord server is also very friendly and helpful.</p>

<h2 id="next-time">Next time…</h2>

<p>We will do some actual development!</p>]]></content><author><name></name></author><summary type="html"><![CDATA[“If you’ve seen one Forth…you’ve seen one Forth” the old joke goes. There’s something about the Forth language that makes you want to write your own Forth language. Possibly because it attracts the sort of noble pioneers who relish the challenge of carving out new frontiers from the unyielding bedrock of pure machine code, or possibly because it attracts the sorts of people who are easily distracted…]]></summary></entry></feed>