The Digital Cat - amigahttps://www.thedigitalcatonline.com/2021-12-21T08:00:00+00:00Adventures of a curious cat in the land of programmingExploring the Amiga - Part 82019-02-19T14:00:00+01:002021-12-20T08:00:00+00:00Leonardo Giordanitag:www.thedigitalcatonline.com,2019-02-19:/blog/2019/02/19/exploring-the-amiga-8/<p>Branching in the M68k Assembly, memory list management in Kickstart 1.3</p><h2 id="branching-e97e">Branching<a class="headerlink" href="#branching-e97e" title="Permanent link">¶</a></h2><p>As branching is one of the most important ways to control the program flow it is worth discussing how it works in the Motorola 68000 processor, and review what options we have.</p><p>Contrary to what happens in higher level languages, in Assembly we do not have a way to represent logical expressions. In a language like C we can compare two integers with something like <code>a > b</code> which tells us if the first number is greater than the second. But in Assembly there is no such syntax, so we have to rely on processor flags; these are bits that the processor sets according to the result of some operation, be it a direct comparison of two values or another instruction that manages integer numbers.</p><p>The Motorola 68000 flags are kept in the 5 least significant bits of the Status Register (SR). The latter is not available during the normal operations in user mode, but these 5 bits, called Condition Code Register (CCR) are always accessible. The 5 bits are named <code>X</code>, <code>N</code>, <code>Z</code>, <code>V</code>, and <code>C</code>. Of these, the ones we need to take into consideration for branching are the rightmost 4.</p><ul><li><code>N</code> (Negative) is set to the value of the most significant bit of the result of an instruction, to show that a negative number was produced.</li><li><code>Z</code> (Zero) is set if the result is zero.</li><li><code>V</code> (Overflow) is set when an arithmetic operation results in a number that cannot be represented with the size of the operand.</li><li><code>C</code> (Carry) is set when an addition or a subtraction need to carry a bit (carry out or borrow).</li></ul><p>To branch according to the value of these flags we can use the <code>Bcc</code> family of instruction, where <code>cc</code> is a two-letter mnemonic that represents the condition that is under test. For example the condition <code>MI</code> stands for <code>MInus</code> and tests if the <code>N</code> flag is set. Thus, <code>bmi <address></code> will branch to <code><address></code> if the result of the previous instruction was negative, because that instruction set the <code>N</code> flag.</p><p>There are multiple families of instructions that use the CCR: <code>Bcc</code>, <code>DBcc</code>, <code>Scc</code>, and <code>TRAPcc</code>. The following table lists all the possible conditions we can test on the Motorola 68000 and their meaning</p><div class="code"><div class="content"><div class="highlight"><pre>| Instruction | Full name | Tested condition | Notes |
|-------------|------------------|-------------------|-----------------------|
| CC | Carry Clear | C == 0 | |
| CS | Carry Set | C == 1 | |
| EQ | EQual | Z == 1 | |
| F | False | Always false | Not available for Bcc |
| GE | Greater or Equal | N == V | |
| GT | Greater Than | N == V and Z == 0 | |
| HI | HIgher than | C == 0 and Z == 0 | |
| LE | Less or Equal | Z == 1 or N != V | |
| LS | Lower or Same | C == 1 or Z == 1 | |
| LT | Less Than | N != V | |
| MI | MInus | N == 1 | |
| NE | Not Equal | Z == 0 | |
| PL | PLus | N == 0 | |
| T | True | Always true | Not available for Bcc |
| VC | V Clear | V == 0 | |
| VS | V Set | V == 1 | |
|-------------|------------------|-------------------|-----------------------|
</pre></div> </div> </div><p>The reason why the <code>T</code> and <code>F</code> conditions are not available for <code>Bcc</code> instructions is simple: <code>bt</code> would mean "branch always", and the <code>bra</code> instruction does exactly this, while <code>bf</code> would mean "never branch" and it's arguably useless.</p><p>Let's see an example of use with a standard comparison instruction: <code>cmp</code></p><div class="code"><div class="content"><div class="highlight"><pre>cmp.w d0,d1
beq.b 0x1522
</pre></div> </div> </div><p>Here, the processor compares <code>d0</code> and <code>d1</code> computing the subtraction <code>d1 - d0</code> and setting the CCR flags according to the nature of the result. The processors manual (Programmer's Reference Manual, section 4-75, page 179) tells us that the flags are affected according to these rules</p><ul><li><code>X</code> — Not affected.</li><li><code>N</code> — Set if the result is negative; cleared otherwise.</li><li><code>Z</code> — Set if the result is zero; cleared otherwise.</li><li><code>V</code> — Set if an overflow occurs; cleared otherwise.</li><li><code>C</code> — Set if a borrow occurs; cleared otherwise.</li></ul><p>The <code>beq</code> instruction on the second line checks only the <code>Z</code> flag, however, so what we are checking here is if <code>d0</code> and <code>d1</code> have the same value.</p><p>A less straightforward example involves <code>move</code></p><div class="code"><div class="content"><div class="highlight"><pre>move.w 0x1c(a1),d0
beq.b 0x151e
</pre></div> </div> </div><p>The first line moves the value at <code>a1 + 0x1c</code> into <code>d0</code>. The <code>move</code> instruction page (Programmer's Reference Manual, section 4-116, page 220) says that it affects only the <code>N</code> and <code>Z</code> flags</p><ul><li><code>X</code> — Not affected.</li><li><code>N</code> — Set if the result is negative; cleared otherwise.</li><li><code>Z</code> — Set if the result is zero; cleared otherwise.</li><li><code>V</code> — Always cleared.</li><li><code>C</code> — Always cleared.</li></ul><p>Here, the tested result is the value that has been moved. So, the next line branches only if the address <code>a1 + 0x1c</code> (and later <code>d0</code>) contains a zero.</p><p>Pay attention that <code>GE</code>, <code>GT</code>, <code>LE</code>, and <code>LT</code> read <code>N</code>, so they should be used only with signed integers Unsigned integers, instead, should be compared using <code>HI</code> and <code>LS</code>.</p><h2 id="enqueue-bc5b">Enqueue<a class="headerlink" href="#enqueue-bc5b" title="Permanent link">¶</a></h2><p>In the previous article we discussed the structure of a memory node created by <code>AddMemList</code> and I briefly mentioned <code>Enqueue</code> and a protected version of it. <code>Enqueue</code> is the function that Exec uses to add a node into a linked list, following a simple schema based on priorities, but it turns out <code>AddMemList</code> does not use it directly, resorting to a version of it wrapped by two other functions, namely <code>Forbid</code> and <code>Permit</code> that are connected to task rescheduling. For the time being, however, we may forget the wrappers and jump directly to <code>Enqueue</code> and learn how the Amiga operating system was managing linked lists.</p><p>If the list we are managing is a singly linked list, where each node points to the successor only, we are forced to find the node <em>after which</em> we want to insert the new one. This is mandatory, as we have to change its outgoing pointer, redirecting it to the new inserted node. In a doubly linked list such the ones used by the Amiga system this is not a problem, as from any point in the list we can start traversing it either forward or backward.</p><p>The following drawing is a simple representation of what happens in the Kickstart code when the <code>Enqueue</code> function is executed. <code>Ins</code> is the node we want to insert, <code>Pred</code> is the node after which we will insert <code>Ins</code>, and <code>Next</code> is the node before which we will insert <code>Ins</code>.</p><div class="code"><div class="content"><div class="highlight"><pre> TAILPRED TAIL
| |
v v
BEFORE: HEAD -> Node -> Pred -> Next -> Node -> 0x0
TAILPRED TAIL
| |
v v
AFTER: HEAD -> Node -> Pred Next -> Node -> 0x0
| ^
| |
+-> Ins --+
</pre></div> </div> </div><p>As you can see there are several pointers that need to be changed. Both the <code>LN_SUCC</code> of <code>Pred</code> and <code>Ins</code>, but also the <code>LN_PRED</code> of <code>Next</code> and <code>Ins</code>.</p><p><code>Enqueue</code> has a very simple prototype</p><div class="code"><div class="content"><div class="highlight"><pre>Enqueue(list, node)
aO a1
</pre></div> </div> </div><p>where <code>a0</code> and <code>a1</code> are pointers respectively to the list header and to the node we are going to insert. It is worth recalling how the list header status is at the time when the first insertion happens, that is when either the chip or expansion memory are added to the system memory pool, managed through <code>MemList</code>. The latter is a list header <code>LH</code> structure and the actual values are the following</p><div class="code"><div class="content"><div class="highlight"><pre> 0xe +-------+ 0x150
| |
0xd +-------+ 0x14f (LH_pad)
| 0xa |
0xc +-------+ 0x14e (LH_TYPE)
| 0x142 |
0x8 +-------+ 0x14a (LH_TAILPRED)
| 0x0 |
0x4 +-------+ 0x146 (LH_TAIL)
| 0x146 |
0x0 +-------+ 0x142 (LH_HEAD)
</pre></div> </div> </div><p>With this structure in mind, let's dive into the source code of <code>Enqueue</code>. The function is defined at <code>0xfc1670</code></p><div class="code"><div class="content"><div class="highlight"><pre>00001670: 1229 0009 move.b 0x9(a1),d1
00001674: 2010 move.l (a0),d0
00001676: 2040 movea.l d0,a0
00001678: 2010 move.l (a0),d0
0000167a: 6706 beq.b 0x1682
0000167c: b228 0009 cmp.b 0x9(a0),d1
00001680: 6ff4 ble.b 0x1676
00001682: 2028 0004 move.l 0x4(a0),d0
00001686: 2149 0004 move.l a1,0x4(a0)
0000168a: 2288 move.l a0,(a1)
0000168c: 2340 0004 move.l d0,0x4(a1)
00001690: 2040 movea.l d0,a0
00001692: 2089 move.l a1,(a0)
00001694: 4e75 rts
</pre></div> </div> </div><p>And it can be roughly divided into three sections, according to the internal jumps.</p><div class="code"><div class="content"><div class="highlight"><pre>Init:
00001670: 1229 0009 move.b 0x9(a1),d1
00001674: 2010 move.l (a0),d0
FindPos:
00001676: 2040 movea.l d0,a0
00001678: 2010 move.l (a0),d0
0000167a: 6706 beq.b InsertNode
0000167c: b228 0009 cmp.b 0x9(a0),d1
00001680: 6ff4 ble.b FindPos
InsertNode:
00001682: 2028 0004 move.l 0x4(a0),d0
00001686: 2149 0004 move.l a1,0x4(a0)
0000168a: 2288 move.l a0,(a1)
0000168c: 2340 0004 move.l d0,0x4(a1)
00001690: 2040 movea.l d0,a0
00001692: 2089 move.l a1,(a0)
00001694: 4e75 rts
</pre></div> </div> </div><p>The first section, <code>Init</code>, prepares the execution of the rest of the function. The rest bla</p><p>As I did for other functions in the previous articles, I'm going to dissect this line by line. Let's start from the <code>Init</code> part.</p><h3 id="init-866d">Init</h3><div class="code"><div class="content"><div class="highlight"><pre>Init:
00001670: 1229 0009 move.b 0x9(a1),d1
00001674: 2010 move.l (a0),d0
</pre></div> </div> </div><p>Since <code>a1</code> points to the node to be inserted, <code>0x9(a1)</code> is the <code>LN_PRI</code> field of that node, that is the priority. The first line thus stores it in <code>d1</code> because it will be used to search for the insertion point, as the list is maintained in priority order.</p><p>The second line is the core or the node traversal algorithm. Since <code>a0</code> points to a <code>LH</code> structure, it is also the address of the first field <code>LH_HEAD</code>. <code>(a0)</code>, thus, is the dereferencing of that address, which means the address of the first node in the list. Given the figures I showed before for <code>MemList</code>, <code>a0</code> is <code>0x142</code> so a <code>move a0,d0</code> would store <code>0x142</code> (the value of <code>a0</code>) in <code>d0</code>. <code>(a0)</code>, instead, is the content of that address in memory, namely <code>0x146</code>, so <code>move (a0),d0</code> stores <code>0x146</code> (the content of <code>0x142</code>) in <code>d0</code>.</p><p>Putting aside the intricacies of the addressing modes, the second line stores the address of the first node in the list, which is the part of the header that starts with <code>LH_TAIL</code>. The header thus acts as the first node of the list.</p><h3 id="findpos-da2c">FindPos</h3><div class="code"><div class="content"><div class="highlight"><pre>FindPos:
00001676: 2040 movea.l d0,a0
00001678: 2010 move.l (a0),d0
0000167a: 6706 beq.b InsertNode
0000167c: b228 0009 cmp.b 0x9(a0),d1
00001680: 6ff4 ble.b FindPos
</pre></div> </div> </div><p>The first two lines repeat the same algorithm. The address of the current node (<code>d0</code>) is moved to <code>a0</code> and the dereferencing operation <code>(a0)</code> puts in <code>d0</code> the address of the following node. The reason why we do it in two lines is that the Address Register Indirect Mode can be used only with <code>An</code> registers. At this point <code>d0</code> contains the address of the second node, the successor of the header.</p><p>If the list contains at least one node, <code>d0</code> contains its address. But if the list is empty at this point <code>d0</code> contains <code>0x0</code>, and this is the condition tested by the <code>beq.b</code> instruction. If <code>d0</code> is empty we reached the end of the list, which means that there was no better place to insert the node, and we jump to the actual node insertion code, <code>InsertNode</code>. If the value is not zero, the current node has a proper successor, so let's check it's priority to see if we need to go on or if we can stop here. The code compares the priorities of the current node and of the <code>Ins</code> node, and if the latter is less than the former we can loop back to <code>FindPos</code> and move to the next node. Remember that priorities are expressed with negative numbers only, so "less than" actually means "higher priority".</p><h3 id="insertnode-f322">InsertNode</h3><div class="code"><div class="content"><div class="highlight"><pre>InsertNode:
00001682: 2028 0004 move.l 0x4(a0),d0
00001686: 2149 0004 move.l a1,0x4(a0)
0000168a: 2288 move.l a0,(a1)
0000168c: 2340 0004 move.l d0,0x4(a1)
00001690: 2040 movea.l d0,a0
00001692: 2089 move.l a1,(a0)
00001694: 4e75 rts
</pre></div> </div> </div><p>In either case, when we reach the tail or when the priority of the next node is lower than the one of the new node, we reach <code>InsertNode</code>. At this point <code>a0</code> points to <code>Next</code> and we can access <code>Pred</code> through <code>0x4(a0)</code> (that is <code>LN_SUCC</code> of <code>Next</code>).</p><div class="code"><div class="content"><div class="highlight"><pre>00001682: 2028 0004 move.l 0x4(a0),d0
00001686: 2149 0004 move.l a1,0x4(a0)
</pre></div> </div> </div><p>This first stores the aforementioned address of <code>Pred</code> in <code>d0</code>, then replaces it with the value of <code>a1</code>. The result is that the predecessor of <code>Next</code> becomes <code>Ins</code>.</p><p>----m68k 0000168a: 2288 move.l a0,(a1) 0000168c: 2340 0004 move.l d0,0x4(a1) ----</p><p>This moves <code>a0</code>, the address of <code>Next</code>, into the first field of <code>Ins</code>, that is <code>Next</code> becomes the successor of <code>Ins</code>. The second line moves <code>d0</code> (the address of <code>Pred</code>) into the <code>LN_PRED</code> of <code>Ins</code>.</p><div class="code"><div class="content"><div class="highlight"><pre>00001690: 2040 movea.l d0,a0
00001692: 2089 move.l a1,(a0)
00001694: 4e75 rts
</pre></div> </div> </div><p>Last, the address of <code>Ins</code> becomes the <code>LN_SUCC</code> of <code>Pred</code>, so we move <code>d0</code> into <code>a0</code> because, as I already mentioned, the Address Register Indirect Mode can be used only with <code>An</code> registers. After this the function returns to the caller.</p><h2 id="remove-cff0">Remove<a class="headerlink" href="#remove-cff0" title="Permanent link">¶</a></h2><p>Since we reviewed the code of <code>Enqueue</code> it makes sense to have a look at the opposite function, <code>Remove</code>. A protected version of this exists as well, but here I will just show the code of the pure function without the wrapper.</p><p>Removing a node is simpler than adding it, as all we have to do is to make <code>Pred</code> point to <code>Next</code> and vice versa, so the function is much shorter. It's worth noting that while the function accepts the address of the node in <code>a1</code> the value in this register is eventually overwritten, so the address has to be kept elsewhere when the function is called.</p><div class="code"><div class="content"><div class="highlight"><pre>0000163c: 2051 movea.l (a1),a0
0000163e: 2269 0004 movea.l 0x4(a1),a1
00001642: 2288 move.l a0,(a1)
00001644: 2149 0004 move.l a1,0x4(a0)
00001648: 4e75
</pre></div> </div> </div><p>The first two lines store the address of <code>Next</code> in <code>a0</code> and of <code>Pred</code> in <code>a1</code> (overwriting the input value). The third line makes <code>Next</code> the successor of <code>Pred</code> and the fourth line makes <code>Pred</code> the predecessor of <code>Next</code>. Then the function returns to the caller.</p><h2 id="addlibrary-5df4">AddLibrary<a class="headerlink" href="#addlibrary-5df4" title="Permanent link">¶</a></h2><p>In the <a href="https://www.thedigitalcatonline.com/blog/2018/06/25/exploring-the-amiga-6/">6th post</a> of this series we left Kickstart just after it finished adding the physical memory to the system pool. The last instruction we mentioned was a call to <code>AddLibrary</code>, and this is then the next function I will explore.</p><p>The code of the function is at <code>fc1448</code>, and required some work before I was able to read it (see the section "Manual decompilation").</p><div class="code"><div class="content"><div class="highlight"><pre>00001448: 41ee 017a lea 0x17a(a6),a0
0000144c: 6100 0270 bsr.w 0x16be
00001450: 6100 0082 bsr.w 0x14d4
00001454: 4e75 rts
</pre></div> </div> </div><p>The first line loads the absolute address of an object <code>0x17a</code> bytes after the ExecBase address, and looking up this displacement in the library structure published in both the fifth and sixth instalment we find, rather unsurprisingly, that this is the address of <code>LibList</code>.</p><p>The following lines call the protected version of <code>Enqueue</code> and <code>SumLibrary</code>, after which the routine returns to the caller. The call to <code>Enqueue</code> follows what I explained at the beginning of this post, where this time <code>a0</code> points to the library system list and <code>a1</code> points to the base address of Exec, set just before the call to <code>AddLibrary</code>. So the Exec library itself is added to the system libraries through this routine.</p><p><code>SumLibrary</code>, as the name suggests, computes the checksum of a library, or checks the existing one.</p><h2 id="manual-decompilation-7dae">Manual decompilation<a class="headerlink" href="#manual-decompilation-7dae" title="Permanent link">¶</a></h2><p>When I was following the call to <code>AddLibrary</code> from the main body of Kickstart I was surprised to find this code</p><div class="code"><div class="content"><div class="highlight"><pre>00001446: 0000 41ee ori.b #-0x12,d0
0000144a: 017a 6100 bchg d0,0x754c(pc)
0000144e: 0270 6100 0082 andi.w #0x6100,(-0x7e,a0,d0.w)
00001454: 4e75 rts
</pre></div> </div> </div><p>which at first glance doesn't look like a basic function, as the instructions are too convoluted. Furthermore, the branch uses the address <code>0x1448</code>, which is thus supposed to be the beginning of the function. A quick look at the hexadecimal values reveals the truth: at <code>0x1446</code> Kickstart contains a padding word <code>0000</code> that confused the decompiler. To see the code of the function I had to manually decompile the machine code, and since the process is very interesting I decided to show it in detail here.</p><p>When you try to manually decompile some machine code you need a cheat sheet and the processor manual (see the resources section), which can help you to quickly track down the meaning of the single bits. The values we are interested in are</p><div class="code"><div class="content"><div class="highlight"><pre>00001448: 41ee
0000144a: 017a
0000144c: 6100
0000144e: 0270
00001450: 6100
00001452: 0082
00001454: 4e75
</pre></div> </div> </div><p>since <code>fc1456</code> is listed as the address of the <code>RemLibrary</code> function. The binary representation of these values is</p><div class="code"><div class="content"><div class="highlight"><pre>00001448: 0100 0001 1110 1110
0000144a: 0000 0001 0111 1010
0000144c: 0110 0001 0000 0000
0000144e: 0000 0010 0111 0000
00001450: 0110 0001 0000 0000
00001452: 0000 0000 1000 0010
00001454: 0100 1110 0111 0101
</pre></div> </div> </div><p>Now, the first 4 bits of any Motorola 68k instruction are the instruction code. While multiple instructions share the same 4 bits (for example <code>andi</code> and <code>subi</code>), those bits are never used for addressing or to specify modes, so they are a good starting point.</p><p>The instruction at <code>0x1448</code> starts with <code>0100</code> followed by a <code>0</code>, and this narrows the selection to a bunch of instructions: some types of <code>move</code>, <code>negx</code>, <code>clr</code>, <code>neg</code>, <code>not</code>, <code>lea</code>, <code>chk</code>. Among these, only <code>lea</code> or <code>chk</code> can be followed by <code>0001</code>.</p><p>Both instructions use the following 3 bits to specify a register (<code>An</code> for <code>lea</code>, <code>Dn</code> for <code>chk</code>), but then the first has a fixed group <code>111</code>, while the second has a group <code>110</code>. This means that we are looking at a <code>lea</code>.</p><p>The three bits after <code>0100</code> are <code>000</code>, which translates to <code>a0</code> as a target. The last 6 bits are the addressing mode and the register, and the cheat sheet tells us that <code>101 110</code> corresponds to Address Register Indirect with Displacement Mode on <code>a6</code>. <code>101</code> is labelled as <code>(d16, An)</code>, while <code>110</code> is the number 6. The following word is thus the <code>d16</code> displacement from <code>a6</code>, which means that <code>41ee 017a</code> translates to <code> lea 0x17a(a6),a0</code>.</p><div class="code"><div class="content"><div class="highlight"><pre>lea a0 (d16,An) 0x17a
| | | |
v v v |-----------------|
0100 000 111 101 110 0000 0001 0111 1010
^ ^
| |
fixed a6
00001448: 41ee 017a lea 0x17a(a6),a0
</pre></div> </div> </div><p>The second instruction starts at <code>0x144c</code> with <code>0110</code>, which is the signature of all the branch commands: <code>bra</code>, <code>bsr</code>, and all the condition-based ones like <code>bgt</code>, <code>blt</code>, and so on. Since the following 4 bits are <code>0001</code> we know this is a <code>bsr</code>, branch to subroutine. Now, in this instruction the 8 least significant bits tell us what the displacement is, and thus the type of the operand (Programmer's Manual, section 4-59, page 163). In this case they are all <code>0</code>, which means a word displacement, which is in the next 16 bits. Pay attention that, as we discussed for <code>lea</code> in the <a href="https://www.thedigitalcatonline.com/blog/2018/05/28/exploring-the-amiga-1/">first post</a> the Program Counter contains the address of the first displacement word. In this case the displacement is <code>0x270</code> at address <code>0x144e</code>, so the branch address is the sum of the two, that is <code>0x16be</code>.</p><div class="code"><div class="content"><div class="highlight"><pre> bsr displacement
| |
|-------| |-------|
0110 0001 0000 0000 0000 0010 0111 0000
|-----------------|
|
0x270 (+ 0x144e = 0x16be)
0000144c: 6100 0270 bsr.w 0x16be
</pre></div> </div> </div><p>The third instruction is again a <code>bsr</code> and following the same process we find out that the branch address is the sum between <code>0x82</code> and <code>0x1452</code>, that is <code>0x14d4</code>.</p><div class="code"><div class="content"><div class="highlight"><pre> bsr displacement
| |
|-------| |-------|
0110 0001 0000 0000 0000 0000 1000 0010
|-----------------|
|
0x82 (+ 0x1452 = 0x14d4)
00001450: 6100 0082 bsr.w 0x14d4
</pre></div> </div> </div><p>The final instruction is an <code>rts</code>, as we expected, and as the decompiler correctly told us. The disassembled code is then what we used in the previous section</p><div class="code"><div class="content"><div class="highlight"><pre>00001448: 41ee 017a lea 0x17a(a6),a0
0000144c: 6100 0270 bsr.w 0x16be
00001450: 6100 0082 bsr.w 0x14d4
00001454: 4e75 rts
</pre></div> </div> </div><p>The most important thing to keep in mind when manually reading instructions is the position of the Program Counter. As we already saw two times, with <code>lea</code> and with <code>bsr</code>, the PC moves as soon as the 16-bit instruction has been read, which means that when a displacement is given we have to use the address of the displacement itself as a base for our calculations.</p><h2 id="resources-edc5">Resources<a class="headerlink" href="#resources-edc5" title="Permanent link">¶</a></h2><ul><li>Motorola M68000 Family Programmer's Reference Manual - <a href="https://www.nxp.com/docs/en/reference-manual/M68000PRM.pdf">https://www.nxp.com/docs/en/reference-manual/M68000PRM.pdf</a></li><li>Motorola 68000 Opcodes Cheat Sheet - <a href="http://goldencrystal.free.fr/M68kOpcodes-v2.3.pdf">http://goldencrystal.free.fr/M68kOpcodes-v2.3.pdf</a></li></ul><h2 id="feedback-d845">Feedback<a class="headerlink" href="#feedback-d845" title="Permanent link">¶</a></h2><p>Feel free to reach me on <a href="https://twitter.com/thedigicat">Twitter</a> if you have questions. The <a href="https://github.com/TheDigitalCatOnline/blog_source/issues">GitHub issues</a> page is the best place to submit corrections.</p>Exploring the Amiga - Part 72019-02-19T13:00:00+01:002021-02-26T08:00:00+00:00Leonardo Giordanitag:www.thedigitalcatonline.com,2019-02-19:/blog/2019/02/19/exploring-the-amiga-7/<p>The complete Exec vector table, list management in Kickstart 1.3</p><h2 id="the-complete-exec-vector-table-a081">The complete Exec vector table<a class="headerlink" href="#the-complete-exec-vector-table-a081" title="Permanent link">¶</a></h2><p>In <a href="https://www.thedigitalcatonline.com/blog/2018/06/08/exploring-the-amiga-3/">the 3rd post</a> of this series I showed the Exec vectors table and the way <code>MakeFunctions</code> creates the jump table when Exec is installed in memory. In that post I focused on the first 4 reserved vectors, but it is useful to have the full table while reading the Kickstart code. This is the full vectors table for Exec 34.2 (28 Oct 1987).</p><p>The <strong></strong>Relative offset<strong></strong> column contains the offset of the function in the jump table once the library has been installed, which allows any Amiga program to call Exec functions through the <code>offset(a6)</code> syntax (e.g. <code>OpenLibrary</code> can be called by <code>jsr -0x228(a6)</code>). <strong></strong>Vector position<strong></strong> is the offset of the vector in the Kickstart 1.3 ROM (i.e. <code>0x0001a7c</code> is the position of the vector table itself), <strong></strong>Relative address<strong></strong> is the hexadecimal value stored in the table before the relocation, <strong></strong>Absolute address<strong></strong> is the function position after relocation (i.e. the wrapped sum between the table address and the relative address), and <strong></strong>Function<strong></strong> is the function name according to the include files and the Amiga documentation.</p><div class="code"><div class="content"><div class="highlight"><pre>| Relative offset | Vector position | Relative address | Absolute address | Function |
|-----------------|-----------------|------------------|------------------|--------------------------|
| -0x00 | 00001a7c | 08a0 | 0000231c | Open() |
| -0x06 | 00001a7e | 08a8 | 00002324 | Close() |
| -0x12 | 00001a80 | 08ac | 00002328 | Expunge() |
| -0x18 | 00001a82 | 08ac | 00002328 | Reserved for future use |
| -0x1e | 00001a84 | ee6a | 000008e6 | Supervisor() |
| -0x24 | 00001a86 | f420 | 00000e9c | ExitIntr() |
| -0x2a | 00001a88 | f446 | 00000ec2 | Schedule() |
| -0x30 | 00001a8a | 04f8 | 00001f74 | Reschedule() |
| -0x36 | 00001a8c | f4a0 | 00000f1c | Switch() |
| -0x3c | 00001a8e | f4ea | 00000f66 | Dispatch() |
| -0x42 | 00001a90 | f58e | 0000100a | Exception() |
| -0x48 | 00001a92 | f0b0 | 00000b2c | InitCode() |
| -0x4e | 00001a94 | f188 | 00000c04 | InitStruct() |
| -0x54 | 00001a96 | faac | 00001528 | MakeLibrary() |
| -0x5a | 00001a98 | fb36 | 000015b2 | MakeFunctions() |
| -0x60 | 00001a9a | f080 | 00000afc | FindResident() |
| -0x66 | 00001a9c | f0e8 | 00000b64 | InitResident() |
| -0x6c | 00001a9e | 1596 | 00003012 | Alert() |
| -0x72 | 00001aa0 | 08ee | 0000236a | Debug() |
| -0x78 | 00001aa2 | f9ac | 00001428 | Disable() |
| -0x7e | 00001aa4 | f9ba | 00001436 | Enable() |
| -0x84 | 00001aa6 | 051a | 00001f96 | Forbid() |
| -0x8a | 00001aa8 | 0520 | 00001f9c | Permit() |
| -0x90 | 00001aaa | f6e2 | 0000115e | SetSR() |
| -0x96 | 00001aac | f708 | 00001184 | SuperState() |
| -0x9c | 00001aae | f734 | 000011b0 | UserState() |
| -0xa2 | 00001ab0 | f74e | 000011ca | SetIntVector() |
| -0xa8 | 00001ab2 | f794 | 00001210 | AddIntServer() |
| -0xae | 00001ab4 | f7d4 | 00001250 | RemIntServer() |
| -0xb4 | 00001ab6 | f8e0 | 0000135c | Cause() |
| -0xba | 00001ab8 | fc5c | 000016d8 | Allocate() |
| -0xc0 | 00001ac0 | fcc4 | 00001740 | Deallocate() |
| -0xc6 | 00001abc | fd54 | 000017d0 | AllocMem() |
| -0xcc | 00001abe | fe00 | 0000187c | AllocAbs() |
| -0xd2 | 00001ac0 | fdb0 | 0000182c | FreeMem() |
| -0xd8 | 00001ac2 | fe90 | 0000190c | AvailMem() |
| -0xde | 00001ac4 | fede | 0000195a | AllocEntry() |
| -0xe4 | 00001ac6 | ff6c | 000019e8 | FreeEntry() |
| -0xea | 00001ac8 | fb6c | 000015e8 | Insert() |
| -0xf0 | 00001aca | fb98 | 00001614 | AddHead() |
| -0xf6 | 00001acc | fba8 | 00001624 | AddTail() |
| -0xfc | 00001ace | fbc0 | 0000163c | Remove() |
| -0x102 | 00001ad0 | fbce | 0000164a | RemHead() |
| -0x108 | 00001ad2 | fbde | 0000165a | RemTail() |
| -0x10e | 00001ad4 | fbf4 | 00001670 | Enqueue() |
| -0x114 | 00001ad6 | fc1a | 00001696 | FindName() |
| -0x11a | 00001ad8 | 0208 | 00001c84 | AddTask() |
| -0x120 | 00001ada | 02b4 | 00001d30 | RemTask() |
| -0x126 | 00001adc | 0334 | 00001db0 | FindTask() |
| -0x12c | 00001ade | 0388 | 00001e04 | SetTaskPri() |
| -0x132 | 00001ae0 | 03e2 | 00001e5e | SetSignal() |
| -0x138 | 00001ae2 | 03d8 | 00001e54 | SetExcept() |
| -0x13e | 00001ae4 | 0490 | 00001f0c | Wait() |
| -0x144 | 00001ae6 | 0408 | 00001e84 | Signal() |
| -0x14a | 00001ae8 | 0584 | 00002000 | AllocSignal() |
| -0x150 | 00001aea | 05bc | 00002038 | FreeSignal() |
| -0x156 | 00001aec | 054e | 00001fca | AllocTrap() |
| -0x15c | 00001aee | 0574 | 00001ff0 | FreeTrap() |
| -0x162 | 00001af0 | 00d8 | 00001b54 | AddPort() |
| -0x168 | 00001af2 | 00f0 | 00001b6c | RemPort() |
| -0x16e | 00001af4 | 00f4 | 00001b70 | PutMsg() |
| -0x174 | 00001af6 | 016e | 00001bea | GetMsg() |
| -0x17a | 00001af8 | 019c | 00001c18 | ReplyMsg() |
| -0x180 | 00001afa | 01b6 | 00001c32 | WaitPort() |
| -0x186 | 00001afc | 01de | 00001c5a | FindPort() |
| -0x18c | 00001afe | f9cc | 00001448 | AddLibrary() |
| -0x192 | 00001b00 | f9da | 00001456 | RemLibrary() |
| -0x198 | 00001b02 | f9f0 | 0000146c | OldOpenLibrary() |
| -0x19e | 00001b04 | fa26 | 000014a2 | CloseLibrary() |
| -0x1a4 | 00001b06 | fa3a | 000014b6 | SetFunction() |
| -0x1aa | 00001b08 | fa58 | 000014d4 | SumLibrary() |
| -0x1b0 | 00001b0a | ec14 | 00000690 | AddDevice() |
| -0x1b6 | 00001b0c | ec22 | 0000069e | RemDevice() |
| -0x1bc | 00001b0e | ec26 | 000006a2 | OpenDevice() |
| -0x1c2 | 00001b10 | ec74 | 000006f0 | CloseDevice() |
| -0x1c8 | 00001b12 | ec9c | 00000718 | DoIO() |
| -0x1ce | 00001b14 | ec8a | 00000706 | SendIO() |
| -0x1d4 | 00001b16 | ed0e | 0000078a | CheckIO() |
| -0x1da | 00001b18 | ecb2 | 0000072e | WaitIO() |
| -0x1e0 | 00001b1a | ed2a | 000007a6 | AbortIO() |
| -0x1e6 | 00001b1c | 01e8 | 00001c64 | AddResource() |
| -0x1ec | 00001b1e | 01f0 | 00001c6c | RemResource() |
| -0x1f2 | 00001b20 | 01f4 | 00001c70 | OpenResource() |
| -0x1f8 | 00001b22 | 07b8 | 00002234 | execPrivate7() |
| -0x1fe | 00001b24 | 07c2 | 0000223e | execPrivate8() |
| -0x204 | 00001b26 | 07ee | 0000226a | execPrivate9() |
| -0x20a | 00001b28 | 06a8 | 00002124 | RawDoFmt() |
| -0x210 | 00001b2a | f700 | 0000117c | GetCC() |
| -0x216 | 00001b2c | fdda | 00001856 | TypeOfMem() |
| -0x21c | 00001b2e | 131c | 00002d98 | Procure() |
| -0x222 | 00001b30 | 1332 | 00002dae | Vacate() |
| -0x228 | 00001b32 | f9f8 | 00001474 | OpenLibrary() |
| -0x22e | 00001b34 | 1354 | 00002dd0 | InitSemaphore() |
| -0x234 | 00001b36 | 1374 | 00002df0 | ObtainSemaphore() |
| -0x23a | 00001b38 | 13c4 | 00002e40 | ReleaseSemaphore() |
| -0x240 | 00001b3a | 1428 | 00002ea4 | AttemptSemaphore() |
| -0x246 | 00001b3c | 1458 | 00002ed4 | ObtainSemaphoreList() |
| -0x24c | 00001b3e | 14ce | 00002f4a | ReleaseSemaphoreList() |
| -0x252 | 00001b40 | 14f4 | 00002f70 | FindSemaphore() |
| -0x258 | 00001b42 | 14e4 | 00002f60 | AddSemaphore() |
| -0x25e | 00001b44 | 14f0 | 00002f6c | RemSemaphore() |
| -0x264 | 00001b46 | effc | 00000a78 | SumKickData() |
| -0x26a | 00001b48 | ffaa | 00001a26 | AddMemList() |
| -0x270 | 00001b4a | 1504 | 00002f80 | CopyMem() |
| -0x276 | 00001b4c | 1500 | 00002f7c | CopyMemQuick() |
|-----------------|-----------------|------------------|------------------|--------------------------|
</pre></div> </div> </div><h2 id="the-memory-list-header-93c7">The memory list header<a class="headerlink" href="#the-memory-list-header-93c7" title="Permanent link">¶</a></h2><p>Before we dig into the code of the <code>AddMemList</code> function to see how the memory space is added to the free memory list, let's have a look at the status of the memory list itself, as we need to be familiar with its structure to understand the rest of the process.</p><p>You might recall from the <a href="https://www.thedigitalcatonline.com/blog/2018/06/25/exploring-the-amiga-5/">fifth article</a> that the <code>MemList</code> structure is created <code>0x142</code> bytes after ExecBase, and that the structure is the following</p><div class="code"><div class="content"><div class="highlight"><pre> 0xe +-------+ 0x150
| |
0xd +-------+ 0x14f (LH_pad)
| 0xa |
0xc +-------+ 0x14e (LH_TYPE)
| 0x142 |
0x8 +-------+ 0x14a (LH_TAILPRED)
| 0x0 |
0x4 +-------+ 0x146 (LH_TAIL)
| 0x146 |
0x0 +-------+ 0x142 (LH_HEAD)
</pre></div> </div> </div><p>This means that we can access the memory list with <code>lea 0x142(a6),a0</code>, as <code>0x142</code> is the relative address, i.e. assuming ExecBase is 0. Once the absolute address is in one of the address registers we can access the first node through <code>(a0)</code>, which is the Motorola Assembly version of the C pointer dereference operation (Address Register Indirect Mode). With <code>(a0)</code> we do not use the content of <code>a0</code>, but the content of the memory location which address is contained in <code>a0</code>.</p><p>So if <code>a0</code> is <code>0x142</code> in the previous figure, <code>(a0)</code> is <code>0x0</code> (<code>0x142</code> contains <code>0x146</code>, <code>0x146</code> contains <code>0x0</code>). It's thus convenient to think of <code>a0</code> as the pointer, and of <code>(a0)</code> as the value.</p><p>It is also interesting to note that the structure at <code>0x146</code> is very similar to the <code>LN</code> structure. Recall that the latter is defined in <code>include_i/exec/nodes.i</code> as</p><div class="code"><div class="content"><div class="highlight"><pre> STRUCTURE LN,0 ; List Node
APTR LN_SUCC ; Pointer to next (successor)
APTR LN_PRED ; Pointer to previous (predecessor)
UBYTE LN_TYPE
BYTE LN_PRI ; Priority, for sorting
APTR LN_NAME ; ID string, null terminated
LABEL LN_SIZE ; Note: word aligned
</pre></div> </div> </div><p>and as you can see <code>LH_TAIL</code>, <code>LH_TAILPRED</code>, and <code>LH_TYPE</code> can act as a proper node of a linked list. This is done on purpose (obviously), as the tail of the list has to be processed by the code that manages linked lists.</p><h2 id="adding-free-memory-to-the-system-list-9656">Adding free memory to the system list<a class="headerlink" href="#adding-free-memory-to-the-system-list-9656" title="Permanent link">¶</a></h2><p>At the end of the <a href="https://www.thedigitalcatonline.com/blog/2018/06/25/exploring-the-amiga-6/">previous post</a> we left Exec just after its code prepared the parameters of the free memory that was available on the system. As we saw in that post, this operation is always executed for the chip memory, and optionally for the fast memory if the hardware expansion is installed.</p><p>The <code>AddMemList</code> function at <code>0x1a26</code> is called with the following prototype</p><div class="code"><div class="content"><div class="highlight"><pre>size = AddMemList(size, attributes, pri, base, name)
D0 D0 D1 D2 A0 A1
</pre></div> </div> </div><p>with the purpose of adding the given memory <code>size</code>, starting from the <code>base</code> address, to the system pool. The memory <code>attributes</code> will be store in the memory block and the priority (<code>pri</code>) will be used to find the insertion point. The name is also added to the memory node to identify it.</p><p>We can split the memory addition in two different parts. <code>AddMemList</code> will create a node that contain the parameters of the memory region, then will call <code>Enqueue</code> to add it to the <code>MemList</code> linked list. When Exec bootstraps the system the memory list is empty, but these functions must work in a generic case. Actually when the system has a memory expansion (fast memory), this is the first that is added to the list, but it's immediately followed by the chip memory.</p><p>To help you to follow what happens in the function, this is a depiction of the memory area that we want to add to the system pool at the end of <code>AddMemList</code>, just before the call to <code>Enqueue</code>. The area contains a header made of three structures, <code>LN</code> (linked list node), <code>MH</code> (memory header), and <code>MC</code> (memory chunk)</p><div class="code"><div class="content"><div class="highlight"><pre> SIZE +---------------+
| Free memory |
+---------------+
| ... |
+---------------+
| Free memory |
0x2c +---------------+ <-+
| MC_SIZE | |
0x28 +---------------+ |
| MC_BYTES | | MC
0x24 +---------------+ |
| MC_NEXT | |
0x20 +---------------+ <-+
| MH_FREE | |
0x1c +---------------+ |
| MH_UPPER | |
0x18 +---------------+ |
| MH_LOWER | | MH
0x14 +---------------+ |
| MH_FIRST | |
0x10 +---------------+ |
| MH_ATTRIBUTES | |
0xe +---------------+ <-+
| LN_NAME | |
0xa +---------------+ |
| LN_PRI | |
0x9 +---------------+ |
| LN_TYPE | | LN
0x8 +---------------+ |
| LN_PRED | |
0x4 +---------------+ |
| LN_SUCC | |
0x0 +---------------+ <-+
</pre></div> </div> </div><p>It is interesting to note that, since we are managing the system memory, we cannot store information about it other than in the memory itself. The presence of management structures, then, is reducing the amount of free memory. This has to taken into account when designing a memory management system, but will not be considered in this article.</p><p>When we create the structure shown in figure, we need to ensure that the actual free memory is a multiple of a long word (8 bytes), as this is generally desirable. The memory space we are dealing with starts from 0 in the picture, but can actually be anywhere in the system memory, so it is not guaranteed that either the beginning or the end of the free memory space are aligned with long words.</p><p>The code of <code>AddMemList</code> performs the alignment in two steps. First it aligns the address of <code>MC</code> to the upper nearest long word (multiple of 8), then aligns the total size to the lower nearest long word. For example, if the starting point was 0 and the total size 64 bytes, we would leave all the values untouched:<code>LN_SUCC</code> at <code>0x0</code>, <code>MC_NEXT</code> at <code>0x20</code>, and the total size of the chunk (structure 'MC' + free memory) would be 32 bytes.</p><div class="code"><div class="content"><div class="highlight"><pre> 0x40 +---------------+ <-+
| Free memory | |
+---------------+ | 32 bytes
| MC | |
0x20 +---------------+ <-+
| MH | |
+---------------+ | 32 bytes
| LN | |
0x0 +---------------+ <-+
</pre></div> </div> </div><p>If the starting point was 1 and the total size 65 bytes, however, the result would be: <code>LN_SUCC</code> at <code>0x1</code>, <code>MC_NEXT</code> at <code>0x28</code> (upper long word), and the total size of the memory would be 56 bytes (65-7 gives 58, rounded down to a multiple of 8), which means a chunk of 24 bytes</p><div class="code"><div class="content"><div class="highlight"><pre> 0x42 +---------------+ <-+
| Ignored | | 2 bytes
0x40 +---------------+ <-+
| Free memory | |
+---------------+ | 24 bytes
| MC | |
0x28 +---------------+ <-+
| EMPTY | | 7 bytes
0x21 +---------------+ <-+
| MH | |
+---------------+ | 32 bytes
| LN | |
0x1 +---------------+ <-+
</pre></div> </div> </div><h2 id="addmemlist-internals-1816">AddMemList internals<a class="headerlink" href="#addmemlist-internals-1816" title="Permanent link">¶</a></h2><p>According to the Exec vectors table, <code>AddMemList</code> can be found at <code>00001a26</code>, and ends at <code>00001a78</code>. It is immediately followed by a padding word <code>0000</code> and the vectors table itself</p><div class="code"><div class="content"><div class="highlight"><pre>00001a26: 2149 000a move.l a1,0xa(a0)
00001a2a: 43e8 0020 lea 0x20(a0),a1
00001a2e: 117c 000a 0008 move.b #0xa,0x8(a0)
00001a34: 1142 0009 move.b d2,0x9(a0)
00001a38: 3141 000e move.w d1,0xe(a0)
00001a3c: 2209 move.l a1,d1
00001a3e: 5e81 addq.l #0x7,d1
00001a40: 0201 00f8 andi.b #-0x8,d1
00001a44: c389 exg d1,a1
00001a46: 9289 sub.l a1,d1
00001a48: d081 add.l d1,d0
00001a4a: 0200 00f8 andi.b #-0x8,d0
00001a4e: 0480 0000 0020 subi.l #0x20,d0
00001a54: 2149 0010 move.l a1,0x10(a0)
00001a58: 2149 0014 move.l a1,0x14(a0)
00001a5c: 2209 move.l a1,d1
00001a5e: d280 add.l d0,d1
00001a60: 2141 0018 move.l d1,0x18(a0)
00001a64: 2140 001c move.l d0,0x1c(a0)
00001a68: 2340 0004 move.l d0,0x4(a1)
00001a6c: 4291 clr.l (a1)
00001a6e: 2248 movea.l a0,a1
00001a70: 41ee 0142 lea 0x142(a6),a0
00001a74: 6100 fc48 bsr.w 0x16be
00001a78: 4e75 rts
</pre></div> </div> </div><p>Let's comment the function line by line. I will refer to the fields shown in the picture above by name, mentioning the offsets for clarity.</p><div class="code"><div class="content"><div class="highlight"><pre>00001a26: 2149 000a move.l a1,0xa(a0)
</pre></div> </div> </div><p>This code stores the address of the memory area name in <code>LN_NAME</code> (<code>0xa(a0)</code>).</p><div class="code"><div class="content"><div class="highlight"><pre>00001a2a: 43e8 0020 lea 0x20(a0),a1
</pre></div> </div> </div><p>This loads the absolute address of the memory chunk (structure <code>MC</code>). The purpose of this is to align the memory chunk to a long word later in the code.</p><div class="code"><div class="content"><div class="highlight"><pre>00001a2e: 117c 000a 0008 move.b #0xa,0x8(a0)
00001a34: 1142 0009 move.b d2,0x9(a0)
00001a38: 3141 000e move.w d1,0xe(a0)
</pre></div> </div> </div><p>This stores <code>0xa</code> in the <code>LN_TYPE</code> field (<code>0x8(a0)</code>). This corresponds to <code>NT_MEMORY</code> (see <code>include_i/exec/nodes.i</code>). It then copies the list priority into the <code>LN_PRI</code> field (<code>0x9(a0)</code>), and the memory attributes in the <code>MH_ATTRIBUTES</code> field (<code>0xe(a0)</code>).</p><div class="code"><div class="content"><div class="highlight"><pre>00001a3c: 2209 move.l a1,d1
00001a3e: 5e81 addq.l #0x7,d1
00001a40: 0201 00f8 andi.b #-0x8,d1
00001a4e: 0480 0000 0020 subi.l #0x20,d0
</pre></div> </div> </div><p>These three lines of code align the address of <code>MC</code> (<code>a1</code>) to a long word, adding 7 and removing the least significant 3 bits. Then the size of the headers (<code>0x20</code>) is removed from the total size of the memory region that was passed as an argument.</p><div class="code"><div class="content"><div class="highlight"><pre>00001a54: 2149 0010 move.l a1,0x10(a0)
00001a58: 2149 0014 move.l a1,0x14(a0)
</pre></div> </div> </div><p>Store the first free memory location (<code>a1</code>) in <code>MH_FIRST</code> (<code>0x10(a0)</code>) and in <code>MH_LOWER</code> (<code>0x14(a0)</code>).</p><div class="code"><div class="content"><div class="highlight"><pre>00001a5c: 2209 move.l a1,d1
00001a5e: d280 add.l d0,d1
00001a60: 2141 0018 move.l d1,0x18(a0)
00001a64: 2140 001c move.l d0,0x1c(a0)
</pre></div> </div> </div><p>The size of the free memory is then added to the first free address and stored in <code>MH_UPPER</code> (<code>0x18(a0)</code>). The size of the free memory is stored in <code>MH_FREE</code> (<code>0x1c(a0)</code>).</p><div class="code"><div class="content"><div class="highlight"><pre>00001a68: 2340 0004 move.l d0,0x4(a1)
00001a6c: 4291 clr.l (a1)
</pre></div> </div> </div><p>This code creates the memory chunk in the free memory area. The size of the chunk is stored in the <code>MC_BYTES</code> field (<code>0x4(a1)</code>), and the <code>MC_NEXT</code> field is cleared.</p><div class="code"><div class="content"><div class="highlight"><pre>00001a6e: 2248 movea.l a0,a1
00001a70: 41ee 0142 lea 0x142(a6),a0
00001a74: 6100 fc48 bsr.w 0x16be
00001a78: 4e75 rts
</pre></div> </div> </div><p>The rest of the code prepares the call to the protected version of <code>Enqueue</code>, copying the address of the node in <code>a1</code> , the absolute address of <code>MemList</code> in <code>a0</code>, and branching to the subroutine. When <code>Enqueue</code> returns, <code>AddMemList</code> terminates and returns as well.</p><h2 id="resources-edc5">Resources<a class="headerlink" href="#resources-edc5" title="Permanent link">¶</a></h2><ul><li>Motorola M68000 Family Programmer's Reference Manual - <a href="https://www.nxp.com/docs/en/reference-manual/M68000PRM.pdf">https://www.nxp.com/docs/en/reference-manual/M68000PRM.pdf</a></li><li>Amiga System Programmers Guide, Abacus - <a href="https://archive.org/details/Amiga_System_Programmers_Guide_1988_Abacus">https://archive.org/details/Amiga_System_Programmers_Guide_1988_Abacus</a></li></ul><h2 id="feedback-d845">Feedback<a class="headerlink" href="#feedback-d845" title="Permanent link">¶</a></h2><p>Feel free to reach me on <a href="https://twitter.com/thedigicat">Twitter</a> if you have questions. The <a href="https://github.com/TheDigitalCatOnline/blog_source/issues">GitHub issues</a> page is the best place to submit corrections.</p>Exploring the Amiga - Part 62018-06-25T13:00:00+01:002021-02-26T08:00:00+00:00Leonardo Giordanitag:www.thedigitalcatonline.com,2018-06-25:/blog/2018/06/25/exploring-the-amiga-6/<p>A review of the functions that perform memory management in Kickstart 1.3 </p><h2 id="memory-initialisation-7e93">Memory initialisation<a class="headerlink" href="#memory-initialisation-7e93" title="Permanent link">¶</a></h2><p>The Amiga has two types of memory. The first one is found on-board and it is traditionally referred to as "Chip Memory". This name comes from the fact that both the CPU and the custom chips have access to it, which also means that all this components have to share the access. This slows down the CPU when the custom chips are using the memory. While they can use DMA to access it without blocking the CPU, the memory cannot be accessed by multiple components at the same time.</p><p>The second type of memory is called "Fast Memory" because the CPU has exclusive access to it, thus providing better performances. It is also referred to as "expansion memory" since it comes with expansion boards.</p><p>At a certain point during boot time Kickstart figures out the types of memory installed in the Amiga machine and puts the expansion memory size in <code>a4</code>. If this register contains a non-zero value, Kickstart knows that an expansion board has been installed and configures the memory accordingly.</p><p>The memory initialisation code is the following</p><div class="code"><div class="content"><div class="highlight"><pre>00000380: 200c move.l a4,d0
00000382: 6724 beq.b 0x3a8
00000384: 41ee 024c lea 0x24c(a6),a0
00000388: 43fa ffa8 lea 0x332(pc),a1
0000038c: 7400 moveq #0,d2
0000038e: 323c 0005 move.w #0x5,d1
00000392: 200c move.l a4,d0
00000394: 9088 sub.l a0,d0
00000396: 0480 0000 1800 subi.l #0x1800,d0
0000039c: 6100 1688 bsr.w 0x1a26
000003a0: 41f8 0400 lea 0x400.w,a0
000003a4: 7000 moveq #0,d0
000003a6: 600a bra.b 0x3b2
000003a8: 41ee 024c lea 0x24c(a6),a0
000003ac: 203c ffff e800 move.l #-0x1800,d0
000003b2: 323c 0003 move.w #0x3,d1
000003b6: 2448 movea.l a0,a2
000003b8: 43fa ff6c lea 0x326(pc),a1
000003bc: 74f6 moveq #-0xa,d2
000003be: d08b add.l a3,d0
000003c0: 9088 sub.l a0,d0
000003c2: 6100 1662 bsr.w 0x1a26
000003c6: 224e movea.l a6,a1
000003c8: 6100 107e bsr.w 0x1448
</pre></div> </div> </div><p>As I did in the previous post I will split the code in parts and replace some addresses with labels to try and make the routine more understandable</p><div class="code"><div class="content"><div class="highlight"><pre>Check_expansion:
00000380: 200c move.l a4,d0
00000382: 6724 beq.b Only_chip
Add_expansion:
00000384: 41ee 024c lea 0x24c(a6),a0
00000388: 43fa ffa8 lea 0x332(pc),a1
0000038c: 7400 moveq #0,d2
0000038e: 323c 0005 move.w #0x5,d1
00000392: 200c move.l a4,d0
00000394: 9088 sub.l a0,d0
00000396: 0480 0000 1800 subi.l #0x1800,d0
0000039c: 6100 1688 bsr.w 0x1a26
000003a0: 41f8 0400 lea 0x400.w,a0
000003a4: 7000 moveq #0,d0
000003a6: 600a bra.b Add_chip
Only_chip:
000003a8: 41ee 024c lea 0x24c(a6),a0
000003ac: 203c ffff e800 move.l #-0x1800,d0
Add_chip:
000003b2: 323c 0003 move.w #0x3,d1
000003b6: 2448 movea.l a0,a2
000003b8: 43fa ff6c lea 0x326(pc),a1
000003bc: 74f6 moveq #-0xa,d2
000003be: d08b add.l a3,d0
000003c0: 9088 sub.l a0,d0
000003c2: 6100 1662 bsr.w 0x1a26
000003c6: 224e movea.l a6,a1
000003c8: 6100 107e bsr.w 0x1448
</pre></div> </div> </div><p>The two addresses <code>0x326</code> and <code>0x332</code> mentioned in the code contain the two zero-terminated strings <code>Chip Memory</code> and <code>Fast Memory</code></p><div class="code"><div class="content"><div class="highlight"><pre>; ################################################################
; 'Chip Memory' string
00000326: 43 ; C
00000327: 68 ; h
00000328: 69 ; i
00000329: 70 ; p
0000032a: 20 ; SP
0000032b: 4d ; M
0000032c: 65 ; e
0000032d: 6d ; m
0000032e: 6f ; o
0000032f: 72 ; r
00000330: 79 ; y
00000331: 00 ; NUL
; ################################################################
; 'Fast Memory' string
00000332: 46 ; F
00000333: 61 ; a
00000334: 73 ; s
00000335: 74 ; t
00000336: 20 ; SP
00000337: 4d ; M
00000338: 65 ; e
00000339: 6d ; m
0000033a: 6f ; o
0000033b: 72 ; r
0000033c: 79 ; y
0000033d: 00 ; NUL
</pre></div> </div> </div><p>Let's analyse the code line by line.</p><div class="code"><div class="content"><div class="highlight"><pre>Check_expansion:
00000380: 200c move.l a4,d0
00000382: 6724 beq.b Only_chip
</pre></div> </div> </div><p>The first thing that Kickstart does is to check if <code>a4</code> contains a non-zero value, which signals that we have a memory expansion board available. If <code>a4</code> is zero the code jumps to the part that initialises chip memory only.</p><div class="code"><div class="content"><div class="highlight"><pre>Add_expansion:
00000384: 41ee 024c lea 0x24c(a6),a0
00000388: 43fa ffa8 lea 0x332(pc),a1
</pre></div> </div> </div><p>The code then loads two effective addresses. The first one is the first free memory location (<code>0x24c</code>) and the second one is the string <code>Fast Memory</code>. The reason behind the address <code>0x24c</code> is explained in a later section in detail.</p><p>The purpose of the code is to call the <code>AddMemList</code> routine, which adds the memory to the system free memory pool. It has the following prototype</p><div class="code"><div class="content"><div class="highlight"><pre>size = AddMemList(size, attributes, pri, base, name)
D0 D0 D1 D2 A0 A1
</pre></div> </div> </div><p>where <code>size</code> is the size of the memory (bytes), <code>attributes</code> contains flags that identify memory attributes, <code>pri</code> is the priority of the memory, <code>base</code> is the base address of the new area and <code>name</code> is a name for this list.</p><div class="code"><div class="content"><div class="highlight"><pre>0000038c: 7400 moveq #0,d2
0000038e: 323c 0005 move.w #0x5,d1
</pre></div> </div> </div><p>The code then prepares the registers for the <code>AddMemList</code> call. It gives this memory priority <code>0</code> and sets the <code>attributes</code> flags to <code>0x5</code>, which is <code>101</code>, or <code>PUBLIC</code> and <code>FAST</code> (see <code>include_i/exec/memory.i</code>).</p><div class="code"><div class="content"><div class="highlight"><pre>00000392: 200c move.l a4,d0
00000394: 9088 sub.l a0,d0
00000396: 0480 0000 1800 subi.l #0x1800,d0
0000039c: 6100 1688 bsr.w 0x1a26
</pre></div> </div> </div><p>The expansion memory base address is copied in <code>d0</code> (this is actually an unneeded repetition of what the code did 6 lines before, I think). It then subtracts the address of the first free location (because if this is not 0 it means that something is already stored in memory) and the size of the stack, that was initialised previously by Kickstart to 6 KBytes (hardcoded). After that the code jumps to <code>AddMemList</code> (<code>0x1a26</code>).</p><p>When the routine returns the code begins the initialisation of the chip memory. The chip memory has to be initialised in two different ways depending on the presence of the expansion memory, as the latter is preferably used by the CPU.</p><div class="code"><div class="content"><div class="highlight"><pre>000003a0: 41f8 0400 lea 0x400.w,a0
000003a4: 7000 moveq #0,d0
000003a6: 600a bra.b Add_chip
Only_chip:
000003a8: 41ee 024c lea 0x24c(a6),a0
000003ac: 203c ffff e800 move.l #-0x1800,d0
</pre></div> </div> </div><p>If the initial test on the presence of the expansion memory fails the code jumps directly to <code>0x03a8</code>. If the expansion memory has already been initialised, instead, the CPU executes the code at <code>0x03a0</code> and then jumps to <code>0x03b2</code> (Renamed <code>Add_chip</code> here).</p><div class="code"><div class="content"><div class="highlight"><pre>000003a0: 41f8 0400 lea 0x400.w,a0
</pre></div> </div> </div><p>So if there is expansion memory, Exec will be loaded there, which means that both it and the system stack are not in the chip memory. We can then add the whole space above <code>0x400</code> (more on this number in a later section)</p><div class="code"><div class="content"><div class="highlight"><pre>000003a4: 7000 moveq #0,d0
000003a6: 600a bra.b Add_chip
</pre></div> </div> </div><p>Since the stack has already been created in fast memory we can store a 0 in <code>d0</code> and jump to the code that adds the memory to the system lists</p><p>If the expansion is not present, instead, Exec is installed in the chip memory, so we compute the base like we did for the fast memory.</p><div class="code"><div class="content"><div class="highlight"><pre>Only_chip:
000003a8: 41ee 024c lea 0x24c(a6),a0
000003ac: 203c ffff e800 move.l #-0x1800,d0
</pre></div> </div> </div><p>Here we load the effective address of the first free location, <code>0x24c</code> bytes after the ExecBase address, and we specify the size of the memory as 6 Kbytes less than the maximum, to keep some space for the system stack. The size is negative as later the memory size will be added to the register.</p><div class="code"><div class="content"><div class="highlight"><pre>Add_chip:
000003b2: 323c 0003 move.w #0x3,d1
000003b6: 2448 movea.l a0,a2
000003b8: 43fa ff6c lea 0x326(pc),a1
000003bc: 74f6 moveq #-0xa,d2
000003be: d08b add.l a3,d0
000003c0: 9088 sub.l a0,d0
000003c2: 6100 1662 bsr.w 0x1a26
</pre></div> </div> </div><p>After this we repeat the same procedure that was described for the fast memory. The attributes are now <code>CHIP</code> and <code>PUBLIC</code> (<code>0x3</code>), the string at <code>0x326</code> is <code>Chip Memory</code>, and the priority is -10 (<code>-0xa</code>). The <code>a3</code> register already contains the end address of the chip memory, so we add it to <code>d0</code> and then subtract the first free location computed before to get the size of the memory. As happened before, the routine calls <code>AddMemList</code> at <code>0x1a26</code>.</p><div class="code"><div class="content"><div class="highlight"><pre>000003c6: 224e movea.l a6,a1
000003c8: 6100 107e bsr.w 0x1448
</pre></div> </div> </div><p>The last action of this part of the code is to call <code>AddLibrary</code> at <code>0x1448</code>. The only parameter the routine requires is the base address of the library in <code>a1</code>, which is why the code copies <code>a6</code> there.</p><h2 id="the-magic-number-0x24c-a1a2">The "magic number" `0x24c`<a class="headerlink" href="#the-magic-number-0x24c-a1a2" title="Permanent link">¶</a></h2><p>When we discussed the way the memory is initialised we discovered a "magic number" that Kickstart uses to find the first free location in memory</p><div class="code"><div class="content"><div class="highlight"><pre>00000384: 41ee 024c lea 0x24c(a6),a0
</pre></div> </div> </div><p>The first free location, according to this code is <code>0x24c</code> (588) bytes after the Exec base address. The reason behind this number is simple. When the Exec library is installed its structures use exactly 588 bytes, thus that is the address of the first free space in memory.</p><p>It's easy to calculate this number. Here you find the annotated version of the <code>ExecBase</code> structure that I already used in the previous instalment.</p><p>The structure is described in the <code>include_i/exec/execbase.i</code> include file, and I added the displacement in bytes of each field. The first column is the displacement in the <code>ExecBase</code> structure, while the second starts from <code>0x22</code>. The latter comes from the fact that the structure follows an <code>LN</code> structure and a <code>LIB</code> structure, described in the fourth instalment of this series.</p><div class="code"><div class="content"><div class="highlight"><pre>0000 0022 UWORD SoftVer
0002 0024 WORD LowMemChkSum
0004 0026 ULONG ChkBase
0008 002a APTR ColdCapture
000c 002e APTR CoolCapture
0010 0032 APTR WarmCapture
0014 0036 APTR SysStkUpper
0018 003a APTR SysStkLower
001c 003e ULONG MaxLocMem
0020 0042 APTR DebugEntry
0024 0046 APTR DebugData
0028 004a APTR AlertData
002c 004e APTR MaxExtMem
0030 0052 WORD ChkSum
******* Interrupt Related ********************************************
LABEL IntVects
0032 0054 STRUCT IVTBE,IV_SIZE
003e 0060 STRUCT IVDSKBLK,IV_SIZE
004a 006c STRUCT IVSOFTINT,IV_SIZE
0056 0078 STRUCT IVPORTS,IV_SIZE
0062 0084 STRUCT IVCOPER,IV_SIZE
006e 0090 STRUCT IVVERTB,IV_SIZE
007a 009c STRUCT IVBLIT,IV_SIZE
0086 00a8 STRUCT IVAUD0,IV_SIZE
0092 00b4 STRUCT IVAUD1,IV_SIZE
009e 00c0 STRUCT IVAUD2,IV_SIZE
00aa 00cc STRUCT IVAUD3,IV_SIZE
00b6 00d8 STRUCT IVRBF,IV_SIZE
00c2 00e4 STRUCT IVDSKSYNC,IV_SIZE
00ce 00f0 STRUCT IVEXTER,IV_SIZE
00da 00fc STRUCT IVINTEN,IV_SIZE
00e6 0108 STRUCT IVNMI,IV_SIZE
******* Dynamic System Variables *************************************
00f2 0114 APTR ThisTask
00f6 0118 ULONG IdleCount
00fa 011c ULONG DispCount
00fe 0120 UWORD Quantum
0100 0122 UWORD Elapsed
0102 0124 UWORD SysFlags
0104 0126 BYTE IDNestCnt
0105 0127 BYTE TDNestCnt
0106 0128 UWORD AttnFlags
0108 012a UWORD AttnResched
010a 012c APTR ResModules
010e 0130 APTR TaskTrapCode
0112 0134 APTR TaskExceptCode
0116 0138 APTR TaskExitCode
011a 013c ULONG TaskSigAlloc
011e 0140 UWORD TaskTrapAlloc
******* System List Headers (private!) ********************************
0120 0142 STRUCT MemList,LH_SIZE
012e 0150 STRUCT ResourceList,LH_SIZE
013c 015e STRUCT DeviceList,LH_SIZE
014a 016c STRUCT IntrList,LH_SIZE
0158 017a STRUCT LibList,LH_SIZE
0166 0188 STRUCT PortList,LH_SIZE
0174 0196 STRUCT TaskReady,LH_SIZE
0182 01a4 STRUCT TaskWait,LH_SIZE
0190 01b2 STRUCT SoftInts,SH_SIZE*5
01e0 0202 STRUCT LastAlert,4*4
01f0 0212 UBYTE VBlankFrequency
01f1 0213 UBYTE PowerSupplyFrequency
01f2 0214 STRUCT SemaphoreList,LH_SIZE
0200 0222 APTR KickMemPtr
0204 0226 APTR KickTagPtr
0208 022a APTR KickCheckSum
020c 022e UBYTE ExecBaseReserved[10];
0216 0238 UBYTE ExecBaseNewReserved[20];
022a 024c LABEL SYSBASESIZE
</pre></div> </div> </div><p>The include file that I found in the Amiga Developer CD has a slightly different version of this structure that contains fields from higher versions of Exec. This structure can be found in the Amiga System Programmer's Guide, page 308. You can easily find the definitions of values like <code>SH_SIZE</code> in the include files contained in the <code>include_i/exec/</code> directory.</p><p>As you can see the final address is <code>0x24c</code>, which is exactly where the free memory begins (remember that <code>LABEL</code> is a macro and not a field, so it doesn't use space).</p><h2 id="the-magic-numbers-0x676-and-0x400-5194">The "magic numbers" `0x676` and `0x400`<a class="headerlink" href="#the-magic-numbers-0x676-and-0x400-5194" title="Permanent link">¶</a></h2><p>The Motorola 68000 architecture forces to reserve the first 1024 bytes (<code>0x400</code>) for the exception vectors. The table of these vectors can be found in the Programmer's Reference Manual, page B-2, and this is the source for the magic number used when adding the chip memory to the system lists in case an expansion memory is installed.</p><p>The Exec base address, however, is not <code>0x400</code> but <code>0x676</code>. As we already know the library is preceded by the jump table, and since Exec exports 105 functions we use <code>105*6 = 630</code> bytes for the jump vectors. Adding these 630 bytes to the first 1024 reserved for the exception vectors gives 1654 (<code>0x676</code>) as the base address of the library.</p><div class="code"><div class="content"><div class="highlight"><pre> +---------------------------------------+
| First free address |
2242 +---------------------------------------+ 0x8c2 <-+
| ExecBase structure | |
1688 +---------------------------------------+ 0x698 |
| LIB structure | | Exec base
1668 +---------------------------------------+ 0x684 | structure
| LN structure | |
1654 +---------------------------------------+ 0x676 <-+
| Jump vector #105 | |
+---------------------------------------+ |
| [...] | |
+---------------------------------------+ | Exec jump
| Jump vector #2 | | vector table
1030 +---------------------------------------+ 0x406 |
| Jump vector #1 | |
1024 +---------------------------------------+ 0x400 <-+
| End of reserved space | |
+---------------------------------------+ |
| [...] | |
12 +---------------------------------------+ 0xc | 1 Kilobyte
| Vector #2 | | reserved by
8 +---------------------------------------+ 0x8 | the M68k
| Reset Initial Program Counter | | architecture
4 +---------------------------------------+ 0x4 |
| Reset Initial Interrupt Stack Pointer | |
0 +---------------------------------------+ 0x0 <-+
</pre></div> </div> </div><h2 id="whats-next-0c7e">What's next<a class="headerlink" href="#whats-next-0c7e" title="Permanent link">¶</a></h2><p>It's time to show the complete Exec vector table, as we are going to use and analyse all the functions defined there. I will then discuss the structure of the memory list header and its relationship with linked lists. Last I will dissect the code of <code>AddMemList</code> and thus show in detail how the chip and expansion memory areas are added to to free memory pool.</p><h2 id="resources-edc5">Resources<a class="headerlink" href="#resources-edc5" title="Permanent link">¶</a></h2><ul><li>Microprocessor-based system design by Ricardo Gutierrez-Osuna (<a href="http://courses.cs.tamu.edu/rgutier/ceg411_f01/">slides</a>), in particular <a href="http://courses.cs.tamu.edu/rgutier/ceg411_f01/l9.pdf">Lesson 9 - Exception processing</a></li><li>Motorola M68000 Family Programmer's Reference Manual - <a href="https://www.nxp.com/docs/en/reference-manual/M68000PRM.pdf">https://www.nxp.com/docs/en/reference-manual/M68000PRM.pdf</a></li><li>Amiga System Programmers Guide, Abacus - <a href="https://archive.org/details/Amiga_System_Programmers_Guide_1988_Abacus">https://archive.org/details/Amiga_System_Programmers_Guide_1988_Abacus</a></li></ul><h2 id="feedback-d845">Feedback<a class="headerlink" href="#feedback-d845" title="Permanent link">¶</a></h2><p>Feel free to reach me on <a href="https://twitter.com/thedigicat">Twitter</a> if you have questions. The <a href="https://github.com/TheDigitalCatOnline/blog_source/issues">GitHub issues</a> page is the best place to submit corrections.</p>Exploring the Amiga - Part 52018-06-25T12:00:00+01:002021-02-26T08:00:00+00:00Leonardo Giordanitag:www.thedigitalcatonline.com,2018-06-25:/blog/2018/06/25/exploring-the-amiga-5/<p>List management in Kickstart 1.3</p><p>Memory management is always one of the most rich and complex parts of an architecture, mainly because the available amount of memory is always less than what you would like to have. <a href="https://users.ece.cmu.edu/~koopman/titan/rules.html">Always</a>.</p><p>It is also an interesting topic because memory is generally managed by both the hardware and the software. The microprocessor may provide a memory management unit (MMU) that enforces a specific schema and the software has to implement its own algorithms on top of that.</p><p>The Amiga originally run on a Motorola 68k, which doesn't provide any memory management in hardware. This means that there is no way for the processor to block attempts to read memory by a process, a feature that wasn't present on the first Intel x86 processors as well. Intel "solved" the issue with the introduction of the protected mode in the 386 family (even though an initial version was already present on the 286 processors). Motorola provided external MMUs for the 68010 and 68020, while the 68030 and later processor feature an on-chip MMU. </p><p>The Motorola 68k is a 32-bit processor, thus registers and the address bus have that size. The memory, however, is connected to only 24 of the 32 lines of the bus, which means that the total memory space addressable by the processor is a 24-bit space, that gives 16 Megabytes instead of the possible 4 Gigabytes. That amount of memory was however enough for the period when the Amiga was designed. Consider that the most successful model of Amiga, the Amiga 500, had 500 KBytes of memory, sometimes increased to 1 Megabyte through a memory expansion.</p><p>The lack of a memory scheme enforced by the processor means that at boot time the memory is just a flat area that can be addressed directly. As we saw in the previous instalments, Exec creates its own structure in memory, generating the library node and creating the library jump table. This happens in the bigger picture of the machine initialisation, and one of the tasks performed during this initialisation is the setup of the memory management structures.</p><h2 id="the-exec-base-address-aabd">The Exec base address<a class="headerlink" href="#the-exec-base-address-aabd" title="Permanent link">¶</a></h2><p>Every Amiga programmer knows that address <code>0x4</code> contains the Exec base address, and I showed in past instalments how this is used in conjunction with the jump table to call the library functions.</p><p>The reason why the Exec base address is stored there is however seldom mentioned. As a matter of fact that address is not just a random choice.</p><p>The Motorola 68000 family reserves the first Kilobyte of memory for exception vectors, that is code that will be executed when something wrong happens. "Wrong" here means bad at hardware level, from a divide by zero to a bus error. This is enforced by the Motorola 68k architecture and thus is a feature shared by other computers and consoles based on it.</p><p>The first two of these vectors are actually used when the processor is powering-up (or in general when it resets). And the vector number 1 (the second) at offset <code>0x4</code> is the Reset Initial Program Counter.</p><p>After a reset the processor initialises the Program Counter with the address stored at <code>0x4</code> in the memory. When the CPU is switched on, however, the Kickstart ROM is copied in memory, thus the addresses 0 and 4 (first two exception vectors) are the addresses listed in the ROM itself.</p><p>The very first 8 bytes of the Kickstart ROM are</p><div class="code"><div class="content"><div class="highlight"><pre>00000000: 1111
00000002: 4ef9 00fc 00d2 jmp 0xfc00d2.l
</pre></div> </div> </div><p>and you can clearly see that the long word at address <code>0x4</code> is <code>00fc 00d2</code>. This actually corresponds to the address where the initial code of the ROM is</p><div class="code"><div class="content"><div class="highlight"><pre>000000d2: 4ff9 0004 0000 lea 0x40000.l,sp
</pre></div> </div> </div><p>which sets the stack pointer, but I'll keep this analysis for a future post.</p><p>The address <code>0x4</code> is then free to be used during the normal execution, since it is used only during a reset, but in that case whatever we wrote there (the Exec base address) is overwritten by the ROM code.</p><h2 id="list-headers-d070">List headers<a class="headerlink" href="#list-headers-d070" title="Permanent link">¶</a></h2><p>Exec manages memory and resources using <a href="https://en.wikipedia.org/wiki/Linked_list">linked lists</a>. As you know, to manage a linked list we need the address (pointer) of the head, of the tail, and also of the second-to-last element (the tail predecessor), to allow the tail to be detached and replaced. Actually it is evident that, given the convention that the last node is connected to the address 0, the only value we need is the address of the list head. The two additional addresses, however, can greatly simplify the code that manages the list and can greatly increase the performances, avoiding the need of a complete scan of the list to find the last element every time we want to add something to the end of the list.</p><p>The Exec library provides a nice structure to manage lists, <code>LH</code>. The structure is defined in <code>include_i/exec/lists.i</code></p><div class="code"><div class="content"><div class="highlight"><pre> STRUCTURE LH,0
APTR LH_HEAD
APTR LH_TAIL
APTR LH_TAILPRED
UBYTE LH_TYPE
UBYTE LH_pad
LABEL LH_SIZE ;word aligned
</pre></div> </div> </div><p>The only two fields that this structure adds compared to the previous description are <code>LH_TYPE</code>, that unsurprisingly contains the type of the data contained in the list, and <code>LH_pad</code> which is nothing but what the name suggests, a padding that allows the structure to be word aligned.</p><p>We need now to discover where Exec keeps the header for the memory list. Analysing the <code>ExecBase</code> structure contained in <code>include_i/exec/execbase.i</code> we find the following definitions</p><div class="code"><div class="content"><div class="highlight"><pre>******* System List Headers (private!) ********************************
STRUCT MemList,LH_SIZE
STRUCT ResourceList,LH_SIZE
STRUCT DeviceList,LH_SIZE
STRUCT IntrList,LH_SIZE
STRUCT LibList,LH_SIZE
STRUCT PortList,LH_SIZE
STRUCT TaskReady,LH_SIZE
STRUCT TaskWait,LH_SIZE
STRUCT SoftInts,SH_SIZE*5
</pre></div> </div> </div><p>which is exactly what we wanted. When Exec installs itself in the first part of the memory it will also initialise these headers to keep track of the corresponding resources.</p><p>It is interesting to note, however, that the ExecBase structure described in the include files is not used directly, but is more a description of what the code is going to create. This is a bit different from what higher level languages like C use to do. In C you declare a structure, you reserve memory for it, and then access its fields. In Assembly, ultimately, there is no such a concept as a structure and a field. We have only a (flat) memory and addresses.</p><p>Since ExecBase is a description of the structure of Exec once it will be installed in memory it is interesting to run through its fields and annotate the relative address of each of them.</p><p>I took the code contained in <code>include_i/exec/execbase.i</code> and I computed the address of each field. The first column contains the relative address inside the structure (thus starting from <code>0x0</code>), while the second column contains the address relative to the Exec base address. As shown in the fourth post the <code>LN</code> and <code>LIB</code> structures fill the first 34 bytes, which is why the following starting address is <code>0x22</code></p><div class="code"><div class="content"><div class="highlight"><pre>0000 0022 UWORD SoftVer ; kickstart release number (obs.)
0002 0024 WORD LowMemChkSum ; checksum of 68000 trap vectors
0004 0026 ULONG ChkBase ; system base pointer complement
0008 002a APTR ColdCapture ; coldstart soft capture vector
000c 002e APTR CoolCapture ; coolstart soft capture vector
0010 0032 APTR WarmCapture ; warmstart soft capture vector
0014 0036 APTR SysStkUpper ; system stack base (upper bound)
0018 003a APTR SysStkLower ; top of system stack (lower bound)
001c 003e ULONG MaxLocMem ; top of chip memory
0020 0042 APTR DebugEntry ; global debugger entry point
0024 0046 APTR DebugData ; global debugger data segment
0028 004a APTR AlertData ; alert data segment
002c 004e APTR MaxExtMem ; top of extended mem, or null if none
0030 0052 WORD ChkSum ; for all of the above (minus 2)
******* Interrupt Related ********************************************
LABEL IntVects
0032 0054 STRUCT IVTBE,IV_SIZE
003e 0060 STRUCT IVDSKBLK,IV_SIZE
004a 006c STRUCT IVSOFTINT,IV_SIZE
0056 0078 STRUCT IVPORTS,IV_SIZE
0062 0084 STRUCT IVCOPER,IV_SIZE
006e 0090 STRUCT IVVERTB,IV_SIZE
007a 009c STRUCT IVBLIT,IV_SIZE
0086 00a8 STRUCT IVAUD0,IV_SIZE
0092 00b4 STRUCT IVAUD1,IV_SIZE
009e 00c0 STRUCT IVAUD2,IV_SIZE
00aa 00cc STRUCT IVAUD3,IV_SIZE
00b6 00d8 STRUCT IVRBF,IV_SIZE
00c2 00e4 STRUCT IVDSKSYNC,IV_SIZE
00ce 00f0 STRUCT IVEXTER,IV_SIZE
00da 00fc STRUCT IVINTEN,IV_SIZE
00e6 0108 STRUCT IVNMI,IV_SIZE
******* Dynamic System Variables *************************************
00f2 0114 APTR ThisTask ; pointer to current task (readable)
00f6 0118 ULONG IdleCount ; idle counter
00fa 011c ULONG DispCount ; dispatch counter
00fe 0120 UWORD Quantum ; time slice quantum
0100 0122 UWORD Elapsed ; current quantum ticks
0102 0124 UWORD SysFlags ; misc internal system flags
0104 0126 BYTE IDNestCnt ; interrupt disable nesting count
0105 0127 BYTE TDNestCnt ; task disable nesting count
0106 0128 UWORD AttnFlags ; special attention flags (readable)
0108 012a UWORD AttnResched ; rescheduling attention
010a 012c APTR ResModules ; pointer to resident module array
010e 0130 APTR TaskTrapCode ; default task trap routine
0112 0134 APTR TaskExceptCode ; default task exception code
0116 0138 APTR TaskExitCode ; default task exit code
011a 013c ULONG TaskSigAlloc ; preallocated signal mask
011e 0140 UWORD TaskTrapAlloc ; preallocated trap mask
******* System List Headers (private!) ********************************
0120 0142 STRUCT MemList,LH_SIZE
012e 0150 STRUCT ResourceList,LH_SIZE
013c 015e STRUCT DeviceList,LH_SIZE
014a 016c STRUCT IntrList,LH_SIZE
0158 017a STRUCT LibList,LH_SIZE
0166 0188 STRUCT PortList,LH_SIZE
0174 0196 STRUCT TaskReady,LH_SIZE
0182 01a4 STRUCT TaskWait,LH_SIZE
0190 01b2 STRUCT SoftInts,SH_SIZE*5
01e0 0202 STRUCT LastAlert,4*4
01f0 0212 UBYTE VBlankFrequency ;(readable)
01f1 0213 UBYTE PowerSupplyFrequency ;(readable)
01f2 0214 STRUCT SemaphoreList,LH_SIZE
0200 0222 APTR KickMemPtr ; ptr to queue of mem lists
0204 0226 APTR KickTagPtr ; ptr to rom tag queue
0208 022a APTR KickCheckSum ; checksum for mem and tags
020c 022e UBYTE ExecBaseReserved[10];
0216 0238 UBYTE ExecBaseNewReserved[20];
022a 024c LABEL SYSBASESIZE
</pre></div> </div> </div><p>According to this structure we expect to find the memory list header 322 bytes (<code>0x142</code>) after the base address, which means that this number should be mentioned somewhere in the Kickstart code.</p><p>It is not surprise indeed that the function <code>AllocMem</code> mentions it. This function is part of the Exec API that we explored in the third and fourth instalments. Following the same method described there I found the function at the address <code>0x17d0</code> in the Kickstart code</p><div class="code"><div class="content"><div class="highlight"><pre>; memoryBlock = AllocMem(byteSize, attributes)
; d0 d0 d1
000017d0: 522e 0127 addq.b #0x1,0x127(a6)
000017d4: 48e7 3020 movem.l d2-d3/a2,-(sp)
000017d8: 2600 move.l d0,d3
000017da: 2401 move.l d1,d2
000017dc: 45ee 0142 lea 0x142(a6),a2
000017e0: 2452 movea.l (a2),a2
; ...
</pre></div> </div> </div><p>and in this initial part of the function we can clearly see the code that loads the effective address of <code>0x142(a6)</code>. Remember that <code>a6</code> is always supposed to contain the Exec base address.</p><p>The displacement <code>0x142</code> is also mentioned in a table towards the beginning of the code, and this is the part we are really interested in at the moment</p><div class="code"><div class="content"><div class="highlight"><pre>; Initialise list headers
000002b0: 43fa 0020 lea 0x2d2(pc),a1
000002b4: 3019 move.w (a1)+,d0
000002b6: 6700 0086 beq.w 0x33e
000002ba: 41f6 0000 lea (0,a6,d0.w),a0
000002be: 2088 move.l a0,(a0)
000002c0: 5890 addq.l #0x4,(a0)
000002c2: 42a8 0004 clr.l 0x4(a0)
000002c6: 2148 0008 move.l a0,0x8(a0)
000002ca: 3019 move.w (a1)+,d0
000002cc: 1140 000c move.b d0,0xc(a0)
000002d0: 60e2 bra.b 0x2b4
; List headers
000002d2: 0142 000a
000002d6: 0150 0008
000002da: 015e 0003
000002de: 017a 0009
000002e2: 0188 0004
000002e6: 0196 0001
000002ea: 01a4 0001
000002ee: 016c 0002
000002f2: 01b2 000b
000002f6: 01c2 000b
000002fa: 01d2 000b
000002fe: 01e2 000b
00000302: 01f2 000b
00000306: 0214 000f
0000030a: 0000
</pre></div> </div> </div><p>As you can see I formatted the code to show that the values after <code>02d2</code> are data and not code. The disassembler will obviously show you some instructions but they are just misinterpretations of the binary data. AS it is usual in the Kickstart code, we have some procedure working on a set of data and the data is stored immediately after the code itself. The purpose of this procedure is that of creating the initial headers for the linked lists that Exec will use to manage the system resources.</p><p>This table is immediately followed by the table we found in the <a href="https://www.thedigitalcatonline.com/blog/2018/06/08/exploring-the-amiga-3/">third post</a> of this series, when we were looking at the values of the <code>LIB</code> structure.</p><p>Let's comment line by line the code at <code>02b0</code></p><div class="code"><div class="content"><div class="highlight"><pre>; Initialise list headers
000002b0: 43fa 0020 lea 0x2d2(pc),a1
000002b4: 3019 move.w (a1)+,d0
000002b6: 6700 0086 beq.w 0x33e
000002ba: 41f6 0000 lea (0,a6,d0.w),a0
000002be: 2088 move.l a0,(a0)
000002c0: 5890 addq.l #0x4,(a0)
000002c2: 42a8 0004 clr.l 0x4(a0)
000002c6: 2148 0008 move.l a0,0x8(a0)
000002ca: 3019 move.w (a1)+,d0
000002cc: 1140 000c move.b d0,0xc(a0)
000002d0: 60e2 bra.b 0x2b4
</pre></div> </div> </div><div class="code"><div class="content"><div class="highlight"><pre>000002b0: 43fa 0020 lea 0x2d2(pc),a1
</pre></div> </div> </div><p>First of all the code loads the absolute address of <code>0x2d2(pc)</code> in the <code>a1</code> register. This is exactly the beginning of the table, as shown above.</p><div class="code"><div class="content"><div class="highlight"><pre>000002b4: 3019 move.w (a1)+,d0
000002b6: 6700 0086 beq.w 0x33e
</pre></div> </div> </div><p>The code then loads the first value of the table (<code>0142</code>) in <code>d0</code> and increments <code>a1</code>. This suggests that we are looking at a loop. The following instruction is indeed a comparison that jumps to <code>0x33e</code> is the value is <code>0</code>. You can easily see above that the table is terminated by a <code>0000</code>.</p><div class="code"><div class="content"><div class="highlight"><pre>000002ba: 41f6 0000 lea (0,a6,d0.w),a0
</pre></div> </div> </div><p>The register <code>a0</code> is then loaded with the effective address of Exec + <code>d0</code>. This means that we use the value we just read from the table as a pointer. For the first value, then, we are looking at <code>0x142</code> bytes after the beginning of the Exec library, exactly where we expected to find the Memory List header.</p><div class="code"><div class="content"><div class="highlight"><pre>000002be: 2088 move.l a0,(a0)
000002c0: 5890 addq.l #0x4,(a0)
</pre></div> </div> </div><p>An empty linked list has the head pointing to the tail and the tail pointing to zero. To do this we set the content of that address <code>(a0)</code> to the address itself (<code>a0</code>), then we increment it by 4 making it point to the tail.</p><div class="code"><div class="content"><div class="highlight"><pre>000002c2: 42a8 0004 clr.l 0x4(a0)
000002c6: 2148 0008 move.l a0,0x8(a0)
</pre></div> </div> </div><p>The tail itself is then cleared and the tail predecessor is set to be the head.</p><div class="code"><div class="content"><div class="highlight"><pre>000002ca: 3019 move.w (a1)+,d0
000002cc: 1140 000c move.b d0,0xc(a0)
</pre></div> </div> </div><p>The code then fetches the next word from the table (<code>000a</code>) and puts it into a field 12 bytes (<code>0xc</code>) from the beginning of the structure, that is <code>LH_TYPE</code>. The possible values of this byte can be found in <code>include_i/exec/nodes.i</code>, where we find that the value <code>0xa</code> corresponds to <code>NT_MEMORY</code>.</p><div class="code"><div class="content"><div class="highlight"><pre>;------ Node Types for LN_TYPE
NT_UNKNOWN EQU 0
NT_TASK EQU 1 ; Exec task
NT_INTERRUPT EQU 2
NT_DEVICE EQU 3
NT_MSGPORT EQU 4
NT_MESSAGE EQU 5 ; Indicates message currently pending
NT_FREEMSG EQU 6
NT_REPLYMSG EQU 7 ; Message has been replied
NT_RESOURCE EQU 8
NT_LIBRARY EQU 9
NT_MEMORY EQU 10
NT_SOFTINT EQU 11 ; Internal flag used by SoftInts
NT_FONT EQU 12
NT_PROCESS EQU 13 ; AmigaDOS Process
NT_SEMAPHORE EQU 14
NT_SIGNALSEM EQU 15 ; signal semaphores
NT_BOOTNODE EQU 16
NT_KICKMEM EQU 17
NT_GRAPHICS EQU 18
NT_DEATHMESSAGE EQU 19
NT_USER EQU 254 ; User node types work down from here
NT_EXTENDED EQU 255
</pre></div> </div> </div><p>There is only one instruction left</p><div class="code"><div class="content"><div class="highlight"><pre>000002d0: 60e2 bra.b 0x2b4
</pre></div> </div> </div><p>which jumps back to the beginning of this short piece of code. The procedure then keeps looping on the whole table until it reaches the list terminator <code>0000</code>.</p><p>The final content of the memory at <code>0142</code> will be</p><div class="code"><div class="content"><div class="highlight"><pre>00000142: 0000 0146 ; LH_HEAD
00000146: 0000 0000 ; LH_TAIL
0000014a: 0000 0146 ; LH_TAILPRED
0000014d: 0a ; LH_TYPE
</pre></div> </div> </div><p>And the same happens for the remaining 7 lists from <code>ResourceList</code> to <code>TaskWait</code>. After this the Exec lists are initialised.</p><p>According to the values of <code>LN_TYPE</code> the list headers table is the following</p><div class="code"><div class="content"><div class="highlight"><pre>000002d2: 0142 000a ; MemList (NT_MEMORY)
000002d6: 0150 0008 ; ResourceList (NT_RESOURCE)
000002da: 015e 0003 ; DeviceList (NT_DEVICE)
000002de: 017a 0009 ; LibList (NT_LIBRARY)
000002e2: 0188 0004 ; PortList (NT_MSGPORT)
000002e6: 0196 0001 ; TaskReady (NT_TASK)
000002ea: 01a4 0001 ; TaskWait (NT_TASK)
000002ee: 016c 0002 ; IntrList (NT_INTERRUPT)
000002f2: 01b2 000b ; SoftInts[0] (NT_SOFTINT)
000002f6: 01c2 000b ; SoftInts[1] (NT_SOFTINT)
000002fa: 01d2 000b ; SoftInts[2] (NT_SOFTINT)
000002fe: 01e2 000b ; SoftInts[3] (NT_SOFTINT)
00000302: 01f2 000b ; SoftInts[4] (NT_SOFTINT)
00000306: 0214 000f ; SemaphoreList (NT_SIGNALSEM)
0000030a: 0000
</pre></div> </div> </div><h2 id="whats-next-0c7e">What's next<a class="headerlink" href="#whats-next-0c7e" title="Permanent link">¶</a></h2><p>The next instalment of the series will cover the initial part of the system memory initialisation, both the standard one and potential expansions, introducing <code>AddMemList</code> for the first time. It will also discuss the origin of some important numbers used in the Kickstart code, <code>0x24c</code>, <code>0x400</code>, and <code>0x676</code>.</p><h2 id="resources-edc5">Resources<a class="headerlink" href="#resources-edc5" title="Permanent link">¶</a></h2><ul><li>Microprocessor-based system design by Ricardo Gutierrez-Osuna (<a href="http://courses.cs.tamu.edu/rgutier/ceg411_f01/">slides</a>), in particular <a href="http://courses.cs.tamu.edu/rgutier/ceg411_f01/l9.pdf">Lesson 9 - Exception processing</a></li><li>Motorola M68000 Family Programmer's Reference Manual - <a href="https://www.nxp.com/docs/en/reference-manual/M68000PRM.pdf">https://www.nxp.com/docs/en/reference-manual/M68000PRM.pdf</a></li></ul><h2 id="feedback-d845">Feedback<a class="headerlink" href="#feedback-d845" title="Permanent link">¶</a></h2><p>Feel free to reach me on <a href="https://twitter.com/thedigicat">Twitter</a> if you have questions. The <a href="https://github.com/TheDigitalCatOnline/blog_source/issues">GitHub issues</a> page is the best place to submit corrections.</p>Exploring the Amiga - Part 42018-06-14T14:30:00+01:002021-02-26T08:00:00+00:00Leonardo Giordanitag:www.thedigitalcatonline.com,2018-06-14:/blog/2018/06/14/exploring-the-amiga-4/<p>The base functions of the Exec library and the MakeFunctions procedure</p><h2 id="the-exec-base-functions-1b6b">The Exec base functions<a class="headerlink" href="#the-exec-base-functions-1b6b" title="Permanent link">¶</a></h2><p>We found the Kickstart 1.3 (Exec 34.2) vector table at address <code>0x1a7c</code>, and the first 4 entries read</p><div class="code"><div class="content"><div class="highlight"><pre>00001a7c: 08a0
00001a7e: 08a8
00001a80: 08ac
00001a82: 08ac
</pre></div> </div> </div><p>If we translate these relative values into absolute addresses, summing the address of the table itself, we discover the address of the 4 base functions that every Amiga library has to provide, namely <code>Open</code>, <code>Close</code>, <code>Expunge</code>, and a reserved slot that should contain a function that returns 0.</p><h3 id="open-155c">Open</h3><p>The first value is <code>0x08a0</code>, and if we sum this value to the address of the table itself we get <code>0x1a7c + 0x08a0 = 0x231c</code>. At this address we will find the first function defined in the jump table, that is <code>Open</code>.</p><p>The code is the following</p><div class="code"><div class="content"><div class="highlight"><pre>0000231c: 200e move.l a6,d0
0000231e: 526e 0020 addq.w #0x1,0x20(a6)
00002322: 4e75 rts
</pre></div> </div> </div><p>The <code>Open</code> routine expects the address of the library to be in the <code>a6</code> register, and returns the same value in <code>d0</code>. It then adds 1 to the number contained 32 bytes (<code>0x20</code>) after the address of the library itself and then returns. To find out what this number is we can go back again to the NDK and its include files.</p><p>From a previous investigation we know that, once the library has been installed in memory, there are two structures defined one after the other. The first is the <code>LN</code> structure that represents a linked list node, and the second is the <code>LIB</code> structure that represents the library.</p><p>We find the definition of <code>LN</code> in <code>include_i/exec/nodes.i</code></p><div class="code"><div class="content"><div class="highlight"><pre> STRUCTURE LN,0 ; List Node
APTR LN_SUCC ; Pointer to next (successor)
APTR LN_PRED ; Pointer to previous (predecessor)
UBYTE LN_TYPE
BYTE LN_PRI ; Priority, for sorting
APTR LN_NAME ; ID string, null terminated
LABEL LN_SIZE ; Note: word aligned
</pre></div> </div> </div><p>and the definition of <code>LIB</code> in <code>include_i/exewc/libraries.i</code></p><div class="code"><div class="content"><div class="highlight"><pre> STRUCTURE LIB,LN_SIZE
UBYTE LIB_FLAGS ; see below
UBYTE LIB_pad ; must be zero
UWORD LIB_NEGSIZE ; number of bytes before LIB
UWORD LIB_POSSIZE ; number of bytes after LIB
UWORD LIB_VERSION ; major
UWORD LIB_REVISION ; minor
APTR LIB_IDSTRING ; ASCII identification
ULONG LIB_SUM ; the system-calculated checksum
UWORD LIB_OPENCNT ; number of current opens
LABEL LIB_SIZE ; Warning: Size is not a longword multiple!
</pre></div> </div> </div><p>As you can see the latter mentions the former reserving space for it at the beginning (<code>LN_SIZE</code>).</p><p><code>LABEL</code> is a macro that creates an alias for the current size of the structure, and you can find its definition in <code>include_i/exec/types.i</code>. It works in conjunction with the type macros, which increment the global variable <code>SOFFSET</code>. The code <code>LABEL LN_SIZE</code> in the <code>LN</code> structure produces the definition <code>LN_SIZE EQU 14</code>, which is thus a simple marker and does not contribute to the size of the structure itself.</p><p>The comment after the <code>LABEL</code> macro in the <code>LN</code> structure says that the structure is word aligned, and indeed its size is a multiple of a word (2 bytes): 2 <code>APTR</code> (8 bytes) + 1 <code>UBYTE</code> (1 bytes) + 1 <code>BYTE</code> (1 byte) + 1 <code>APTR</code> (4 bytes) = 14 bytes.</p><p>To find the field updated by <code>Open</code> we need to skip 32 bytes. So, after we skip the whole <code>LN</code> structure, we still have 18 bytes to skip into the <code>LIB</code> structure. At that offset we find the <code>LIB_OPENCNT</code> field (remember that <code>UBYTE</code> is 1 byte, <code>UWORD</code> 2 bytes, and <code>APTR</code> and <code>ULONG</code> 4 bytes). This field is, as the comment reads, the "number of current opens". The system counts the number of times a library has been opened because a library with zero active users can safely be removed from memory.</p><p>The last instruction of the <code>Open</code> function is <code>rts</code> (ReTurn from Subroutine) that returns to the instruction after the <code>jsr</code> that called the function.</p><h3 id="close-expunge-and-the-reserved-slot-2f30">Close, Expunge, and the reserved slot</h3><p>Immediately after the definition of the <code>Open</code> function, we find the definition of <code>Close</code>, listed in the vector table as <code>0x08a8</code>, which becomes <code>0x1a7c + 0x08a8 = 0x2324</code>. The next two entries in the vector table contain the same value <code>0x08ac</code>, which translates to the absolute address <code>0x1a7c + 0x08ac = 0x2328</code>. This address is then the location of both the <code>Expunge</code> function and the reserved function that must return 0.</p><p>The code of the <code>Close</code> function is very simple, it just decrements the open counter (<code>0x20</code> in the library). There is no explicit <code>rts</code> as <code>Close</code> uses the adjacent <code>Expunge</code> code for that. Since it's impossible to remove the Exec library in the Amiga system, the <code>Expunge</code> function of the Exec library just returns 0, which is exactly what the reserved function has to do (thus the same address in the vector table), and what <code>Close</code> does after having decremented the open counter.</p><div class="code"><div class="content"><div class="highlight"><pre>; Close
00002324: 536e 0020 subq.w #0x1,0x20(a6)
; Expunge
00002328: 7000 moveq #0,d0
0000232a: 4e75 rts
</pre></div> </div> </div><h2 id="makefunctions-0da5">MakeFunctions<a class="headerlink" href="#makefunctions-0da5" title="Permanent link">¶</a></h2><p>The <code>MakeFunctions</code> routine is used by Exec to create the vector table at the beginning of the library when it is loaded in memory. You might recall that the vector table is created backward from the beginning of the library, thus allowing to use a simpler addressing scheme.</p><p>The prototype of the <code>MakeFunctions</code> routine is</p><div class="code"><div class="content"><div class="highlight"><pre>size = MakeFunctions(addess, vectors, offset)
d0 a0 a1 a2
</pre></div> </div> </div><p>and the code that we found in one of the previous investigations is at offset <code>0x15b2</code></p><div class="code"><div class="content"><div class="highlight"><pre>000015b2: 2f0b move.l a3,-(sp)
000015b4: 7000 moveq #0,d0
000015b6: 220a move.l a2,d1
000015b8: 6716 beq.b 0x15d0
000015ba: 3219 move.w (a1)+,d1
000015bc: 0c41 ffff cmpi.w #-0x1,d1
000015c0: 6722 beq.b 0x15e4
000015c2: 47f2 1000 lea (0,a2,d1.w),a3
000015c6: 210b move.l a3,-(a0)
000015c8: 313c 4ef9 move.w #0x4ef9,-(a0)
000015cc: 5c80 addq.l #0x6,d0
000015ce: 60ea bra.b 0x15ba
000015d0: 2219 move.l (a1)+,d1
000015d2: 0c81 ffff ffff cmpi.l #-0x1,d1
000015d8: 670a beq.b 0x15e4
000015da: 2101 move.l d1,-(a0)
000015dc: 313c 4ef9 move.w #0x4ef9,-(a0)
000015e0: 5c80 addq.l #0x6,d0
000015e2: 60ec bra.b 0x15d0
000015e4: 265f movea.l (sp)+,a3
000015e6: 4e75 rts
</pre></div> </div> </div><p>The code is probably easier to read if we replace the addresses with some labels</p><div class="code"><div class="content"><div class="highlight"><pre>Setup:
000015b2: 2f0b move.l a3,-(sp)
000015b4: 7000 moveq #0,d0
000015b6: 220a move.l a2,d1
000015b8: 6716 beq.b Absolute
Relative:
000015ba: 3219 move.w (a1)+,d1
000015bc: 0c41 ffff cmpi.w #-0x1,d1
000015c0: 6722 beq.b Cleanup
000015c2: 47f2 1000 lea (0,a2,d1.w),a3
000015c6: 210b move.l a3,-(a0)
000015c8: 313c 4ef9 move.w #0x4ef9,-(a0)
000015cc: 5c80 addq.l #0x6,d0
000015ce: 60ea bra.b Relative
Absolute:
000015d0: 2219 move.l (a1)+,d1
000015d2: 0c81 ffff ffff cmpi.l #-0x1,d1
000015d8: 670a beq.b Cleanup
000015da: 2101 move.l d1,-(a0)
000015dc: 313c 4ef9 move.w #0x4ef9,-(a0)
000015e0: 5c80 addq.l #0x6,d0
000015e2: 60ec bra.b Absolute
Cleanup:
000015e4: 265f movea.l (sp)+,a3
000015e6: 4e75 rts
</pre></div> </div> </div><h3 id="setup-226d">Setup</h3><p>The first part of the code is</p><div class="code"><div class="content"><div class="highlight"><pre>Setup:
000015b2: 2f0b move.l a3,-(sp)
000015b4: 7000 moveq #0,d0
000015b6: 220a move.l a2,d1
000015b8: 6716 beq.b Absolute
</pre></div> </div> </div><p>The first thing this code does is to save the <code>a3</code> register in the stack because it will be changed during the routine.</p><div class="code"><div class="content"><div class="highlight"><pre>Setup:
000015b2: 2f0b move.l a3,-(sp)
</pre></div> </div> </div><p>In Assembly, variables are provided by registers and thus are not namespaced, as the registers are the same through the whole program. This is why you should save and restore them and document which one you will change, for instance to return values.</p><p>Secondly, the code sets the <code>d0</code> register to 0.</p><div class="code"><div class="content"><div class="highlight"><pre>000015b4: 7000 moveq #0,d0
</pre></div> </div> </div><p>This register, according to the function prototype, will contain the final size of the jump table, and 0 is a sensible starting value.</p><p>Lastly, the code moves the <code>a2</code> register to <code>d1</code> to be able to manipulate it. </p><div class="code"><div class="content"><div class="highlight"><pre>000015b6: 220a move.l a2,d1
000015b8: 6716 beq.b Absolute
</pre></div> </div> </div><p>This, however, also sets the processor flags according to the value of <code>a2</code> itself, and those flags are used in the next <code>beq.b</code> instruction. If <code>a2</code> contains the value 0 the table is absolute (code at <code>0x15d0</code>, labelled <code>Absolute</code> here) otherwise it is relative (code at <code>0x15ba</code>, labelled <code>Relative</code> here).</p><h3 id="relative-76cf">Relative</h3><p>The second section manages relative jump vectors</p><div class="code"><div class="content"><div class="highlight"><pre>Relative:
000015ba: 3219 move.w (a1)+,d1
000015bc: 0c41 ffff cmpi.w #-0x1,d1
000015c0: 6722 beq.b Cleanup
000015c2: 47f2 1000 lea (0,a2,d1.w),a3
000015c6: 210b move.l a3,-(a0)
000015c8: 313c 4ef9 move.w #0x4ef9,-(a0)
000015cc: 5c80 addq.l #0x6,d0
000015ce: 60ea bra.b Relative
</pre></div> </div> </div><p>This fetches one of the vectors from the address stored in <code>a1</code> (the address of the vector table), immediately incrementing the register value to point at the next vector.</p><div class="code"><div class="content"><div class="highlight"><pre>Relative:
000015ba: 3219 move.w (a1)+,d1
</pre></div> </div> </div><p>then compares it with <code>0xffff</code> (or <code>#-0x1</code>) to see if we reached the end of the table. In that case the code jumps to the fourth section (<code>0x15e4</code>, labelled <code>Cleanup</code> here), otherwise the execution continues with the next instruction</p><div class="code"><div class="content"><div class="highlight"><pre>000015bc: 0c41 ffff cmpi.w #-0x1,d1
000015c0: 6722 beq.b Cleanup
</pre></div> </div> </div><p>The routine then loads the effective address of the relative vector using <code>a2</code> as the base addressing, and stores it in <code>a3</code>. This register now contains the final entry that will be part of the jump table.</p><div class="code"><div class="content"><div class="highlight"><pre>000015c2: 47f2 1000 lea (0,a2,d1.w),a3
</pre></div> </div> </div><p>Since the address of the jump table is contained in <code>a0</code>, the resulting absolute vector is stored there, then the code stores the value <code>0x4ef9</code> which is the code for the <code>jmp</code> instruction (more on this later)</p><div class="code"><div class="content"><div class="highlight"><pre>000015c6: 210b move.l a3,-(a0)
000015c8: 313c 4ef9 move.w #0x4ef9,-(a0)
</pre></div> </div> </div><p>Note that since we are storing the jump table at negative addresses starting from the library's base pointer we have to copy the argument (the address) first and then the function (the code for <code>jmp</code>). The last thing this part of the code does is to add 6 to the size of the jump table (1 word for the instruction, 2 words for the address) and then jumps back to the beginning of the loop.</p><div class="code"><div class="content"><div class="highlight"><pre>000015cc: 5c80 addq.l #0x6,d0
000015ce: 60ea bra.b Relative
</pre></div> </div> </div><h3 id="absolute-2983">Absolute</h3><p>The third section is very similar to the second one, because it performs the same actions, only with absolute addresses instead of relative ones.</p><div class="code"><div class="content"><div class="highlight"><pre>000015d0: 2219 move.l (a1)+,d1
000015d2: 0c81 ffff ffff cmpi.l #-0x1,d1
000015d8: 670a beq.b Cleanup
000015da: 2101 move.l d1,-(a0)
000015dc: 313c 4ef9 move.w #0x4ef9,-(a0)
000015e0: 5c80 addq.l #0x6,d0
000015e2: 60ec bra.b Absolute
</pre></div> </div> </div><p>The only difference with the previous section is that there is no need to load the effective address, as the value contained in <code>d1</code> is already absolute, so the latter can be stored directly.</p><h3 id="cleanup-9400">Cleanup</h3><p>The last section simply restores the stack pointer, popping the value of the <code>a3</code> register where the pointer was previously saved, and returns to the caller.</p><div class="code"><div class="content"><div class="highlight"><pre>000015e4: 265f movea.l (sp)+,a3
000015e6: 4e75 rts
</pre></div> </div> </div><h2 id="self-modifying-code-44b9">Self-modifying code<a class="headerlink" href="#self-modifying-code-44b9" title="Permanent link">¶</a></h2><p>The creation of the jump table performed by the <code>MakeFunctions</code> routine leverages a very powerful feature of any assembly language, that is being able to produce code that self-modifies. This feature comes from a property of machine languages called <a href="https://en.wikipedia.org/wiki/Homoiconicity">homoiconicity</a>.</p><p>The basic idea of homoiconicity is that the language doesn't consider data and code two different things, thus allowing to use the code as an input for routines and to change it programmatically. Pay attention that self-modifying code is just one of the implications of homoiconicity, and not its definition.</p><p>Homoiconicity is a feature rarely provided by languages. It is very powerful, but it basically forces to keep the language at the level of its own AST (Abstract Syntax Tree), which in turn means that you cannot add a proper abstraction, or, if you prefer, a proper high level language, if we identify with this term a computer language that is similar to the human language.</p><p>Famous examples of high level languages that are homoiconic are Lisp and Prolog. Lisp is a language that manages lists and its syntax is based on... lists. This means that you can pass Lisp code to a function and transform it like you would do with standard data.</p><p>Back to the Motorola Assembly code, the line we are interested in is </p><div class="code"><div class="content"><div class="highlight"><pre>000015c8: 313c 4ef9 move.w #0x4ef9,-(a0)
</pre></div> </div> </div><p>This decrements the address contained in <code>a0</code> by 2 bytes, then stores at the resulting address the hexadecimal number <code>0x4ef9</code>.</p><p>The interesting part is that the number <code>0x4ef9</code> has a specific meaning for the Motorola 68000 processor, and namely that of the <code>jmp</code> instruction. This is clearly shown by the tables in the Programmer's Reference Manual (Section 8, Instruction Format Summary, 8-15).</p><p>First we have to convert the number in binary, and we get</p><div class="code"><div class="content"><div class="highlight"><pre>0x4ef9 -> 0100 1110 1111 1001
</pre></div> </div> </div><p>The first 10 bits (<code>0100 1110 11</code>) already identify a <code>jmp</code> instruction. The following 6 bits (<code>111 001</code>) identify the addressing mode, which in this case is <code>Absolute Long</code> or <code>(xxx).L</code> (Programmer's Reference Manual, 4-108). This instruction shall then be followed by a 4 bytes absolute address. Please note that the function is a <code>jmp</code> and not a <code>jsr</code>, which will then not alter the address stored in the stack; a later <code>rts</code> will thus return to the proper called of the function and not to the jump table.</p><p>With that <code>move.w</code>, then, the code writes in memory some Assembly code. Techniques like this have been and are used by games and viruses to obfuscate the code, as a static analysis of the binary will not reveal what will be there only at runtime!</p><h2 id="whats-next-0c7e">What's next<a class="headerlink" href="#whats-next-0c7e" title="Permanent link">¶</a></h2><p>The next article will discuss the reason behind the address <code>0x4</code> used for the Exec base address, and then will move on discussing linked lists and how Exec manages system resources using them.</p><h2 id="resources-edc5">Resources<a class="headerlink" href="#resources-edc5" title="Permanent link">¶</a></h2><ul><li>Motorola M68000 Family Programmer's Reference Manual - <a href="https://www.nxp.com/docs/en/reference-manual/M68000PRM.pdf">https://www.nxp.com/docs/en/reference-manual/M68000PRM.pdf</a></li><li><a href="http://amigadev.elowar.com">AmigaOS Developer Docs</a></li></ul><h2 id="feedback-d845">Feedback<a class="headerlink" href="#feedback-d845" title="Permanent link">¶</a></h2><p>Feel free to reach me on <a href="https://twitter.com/thedigicat">Twitter</a> if you have questions. The <a href="https://github.com/TheDigitalCatOnline/blog_source/issues">GitHub issues</a> page is the best place to submit corrections.</p>Exploring the Amiga - Part 32018-06-08T12:30:00+01:002021-02-26T08:00:00+00:00Leonardo Giordanitag:www.thedigitalcatonline.com,2018-06-08:/blog/2018/06/08/exploring-the-amiga-3/<p>Exec and the Kickstart 1.3 vector table</p><h2 id="exec-and-the-vector-table-8fc8">Exec and the vector table<a class="headerlink" href="#exec-and-the-vector-table-8fc8" title="Permanent link">¶</a></h2><p>The Exec library is the base library of the Amiga system. This library is loaded in memory at boot time, and it is always open and available. Once loaded, it has the same structure of any other library, that is a prefix containing the jump table in reverse order, then the actual code.</p><p>The trick here is that Exec is the library used to load in memory other libraries, so the function that creates the structure in memory of a given library is contained here. To install Exec in memory, thus, we need to use a function which is part of the library itself.</p><p>This is one of the powers of the Assembly language. The property of treating the code as if it was pure data is called homoiconicity, and is something that can be rarely found in other languages. Lisp is a good example of a higher level homoiconic language.</p><p>Back to our vector table, we have to find a way to use the Exec library to install in memory the Exec library itself. The concept is not that complex, actually. The final structure we are trying to achieve is something like this:</p><div class="code"><div class="content"><div class="highlight"><pre>vectors:
function1-vectors
function2-vectors
function3-vectors
function1:
code
[...]
function2:
code
[...]
function3:
code
[...]
</pre></div> </div> </div><p>In this situation we have 3 functions defined at the addresses <code>function1</code>, <code>function2</code>, and <code>function3</code>. Somewhere in the code at the address <code>vectors</code> there is a plain list that contains the addresses of those functions. Since the code can be relocated this list contains offsets relative to the <code>vectors</code> table itself. So the first element of the table will be <code>function1-vectors</code>, that is the subtraction between the two addresses, and so on.</p><p>For example we might have</p><div class="code"><div class="content"><div class="highlight"><pre>0042 0122
0044 01b8
0046 02d1
[...]
0164 code of function1
[...]
01fa code of function2
[...]
0313 code of function3
[...]
</pre></div> </div> </div><p>Where the entry of the table are <code>0x164-0x42 = 0x122</code>, <code>0x1fa-0x42 = 0x1b8</code>, and <code>0x313-0x42 = 0x2d1</code>.</p><p>The vectors table, thus, is the source from which we can calculate the jump table. The code to perform this, however, is contained in one of the functions itself, let's assume it's function number 2</p><div class="code"><div class="content"><div class="highlight"><pre>function1:
code
[...]
function2:
for each address of <table> create
a jump table entry relative to <start>
function3:
code
[...]
vectors:
function1-vectors
function2-vectors
function3-vectors
</pre></div> </div> </div><p>As you can see the function at <code>function2</code> (in this example) depends on a <code><table></code> and a <code><start></code> parameters which will be contained in some register. At this point, since the address <code>function2</code> is known, there can be some code that runs the function on the table contained in the code itself</p><div class="code"><div class="content"><div class="highlight"><pre>setup:
run <function2> on <vectors_offset> and <setup>
function1:
code
[...]
function2:
for each address of the vector table create
a jump table entry relative to START
function3:
code
[...]
vectors:
function1-vectors
function2-vectors
function3-vectors
</pre></div> </div> </div><p>where <code><vectors_offset></code> is a hardcoded offset (as the displacement of <code>vectors</code> relative to <code>setup</code> is known) and <code>setup</code> is the effective address that the <code>setup</code> routine has at runtime.</p><p>This mechanism creates then a library that can install other libraries through a given function, but that can at the same time install itself.</p><h2 id="the-kickstart-vector-table-a451">The Kickstart vector table<a class="headerlink" href="#the-kickstart-vector-table-a451" title="Permanent link">¶</a></h2><p>An actual example of the vector table mechanism can be found in the Kickstart code. Kickstart is the BIOS of the Amiga system, and is loaded at boot time either from disk (Amiga 1000 and some Amiga 3000) or from a ROM.</p><p>The code of Kickstart 1.3 can be found <a href="https://www.romcollector.com/emulators-firmware-i-29.html">here</a> and you can easily disassemble it with vdasm</p><div class="code"><div class="content"><div class="highlight"><pre>$ vda68k Kickstart1.3.rom > Kickstart1.3.asm
</pre></div> </div> </div><p>Inside this code we can see a practical implementation of the mechanism described above.</p><p>The mandatory disclaimer: <strong>to use the Amiga Kickstart ROM images you must own a license.</strong> (see the Resources section).</p><p>When you disassemble some binary code, however, you don't get some nice source code written in a high level language. Well, not with a simple disassembler like vdasm, anyway. What you get is the one to one interpretation of the binary values according to the processor's conventions, and this includes parts of the binary file that are pure data. The disassembler has no way to know if some binary number represents an instruction or a pure number. Moreover, there is no trace of the original labels used by the author(s) of the code, as they are lost in the translation to machine language, when they are converted to pure addresses.</p><p>The practice of understanding how a system works starting from the pure implementation is called "reversing", and personally I consider it one of the most fascinating tasks a programmer can face.</p><p>The purpose of the present investigation is to find the Kickstart 1.3 vector table, and with that to find the position and implementation of the Exec functions. Well, let's start.</p><h3 id="step-1-437f">Step 1</h3><p>I know that <code>MakeFunctions</code> is used to create in memory the structure of Exec itself. So I know that somewhere that function is invoked on the code that I am studying.</p><p>Since one of the parameters of the <code>MakeFunctions</code> routine is the name of the library a good starting point might be a string containing <code>exec.library</code> (which is the standard name of this library in the Amiga system). Once I find that string I can look for a function call that uses its address as a parameter.</p><p>The byte sequence that represents that string (using ASCII) is <code>65 78 65 63 2E 6C 69 62 72 61 72 79</code>. In Kickstart 1.3 the offset of this string is <code>0x00a8</code>.</p><p>Remember that what you see in the disassembled code is not a string. The disassembler tries to convert everything into instructions, so you will find something like</p><div class="code"><div class="content"><div class="highlight"><pre>000000a8: 6578 bcs.b 0x122
000000aa: 6563 bcs.b 0x10f
000000ac: 2e6c 6962 movea.l 0x6962(a4),sp
000000b0: 7261 moveq #0x61,d1
000000b2: 7279 moveq #0x79,d1
</pre></div> </div> </div><p>When looking for strings it's better to use a hexadecimal editor that can show and search in the ASCII representation of the binary code.</p><div class="imageblock"><img src="/images/exploring-the-amiga-3/search-exec-library-string.png"><div class="title">Search exec.library string</div></div><p>We know that Kickstart is loaded at address 0xfc0000 (Amiga System Programmer's Guide, page 67), so all the 16-bit addresses are relative to 0x00fc. The library name pointer is then <code>00fc 00a8</code>.</p><h3 id="step-2-844d">Step 2</h3><p>In the Amiga system all libraries have a specific structure when loaded in memory. Apart from the prefixed jump table, the library code itself is wrapped in a fixed structure that allows us to read and use it.</p><p>Libraries in memory are nodes of a linked list, so the dist thing we expect to find is the structure of the node itself. Then, inside the node, we expect to find the actual library structure.</p><p>The include file <code>include_i/exec/nodes.i</code> tells us that a standard linked list node has the following structure</p><div class="code"><div class="content"><div class="highlight"><pre> STRUCTURE LN,0 ; List Node
APTR LN_SUCC ; Pointer to next (successor)
APTR LN_PRED ; Pointer to previous (predecessor)
UBYTE LN_TYPE
BYTE LN_PRI ; Priority, for sorting
APTR LN_NAME ; ID string, null terminated
LABEL LN_SIZE ; Note: word aligned
</pre></div> </div> </div><p>The two 32-bit pointers <code>LN_SUCC</code> and <code>LN_PRED</code> are created when the node is loaded in memory, so we need to look for the rest of the structure, namely 1 byte with <code>LN_TYPE</code>, 1 byte with <code>LN_PRI</code> and 4 bytes with <code>LN_NAME</code>. From the same file <code>include_i/exec/nodes.i</code> we know that the node type for a library is <code>09</code></p><div class="code"><div class="content"><div class="highlight"><pre>NT_LIBRARY EQU 9
</pre></div> </div> </div><p>So the pattern we are looking for is <code>09XX 00fc 00a8</code>, respectively the node type (<code>09</code>), an unknown priority (<code>XX</code>), and the library name pointer <code>00fc 00a8</code>. We also know that the pattern is likely to be stored towards the beginning of the whole ROM, as one of the first things the library will do is to create its own structure in memory. This last assumption is not to be taken for granted, as the code could easily jump around, but it's a reasonable one.</p><p>In the Kickstart 1.3 code this pattern can be found at offset <code>0x030c</code>.</p><p>![Search library pattern](/images/exploring-the-amiga-3/search-library-pattern.png)</p><p>If this is the correct position of the node structure, we expect to find just after it the structure of the library as described in the include file <code>include_i/exec/libraries.i</code></p><div class="code"><div class="content"><div class="highlight"><pre> STRUCTURE LIB,LN_SIZE
UBYTE LIB_FLAGS ; see below
UBYTE LIB_pad ; must be zero
UWORD LIB_NEGSIZE ; number of bytes before LIB
UWORD LIB_POSSIZE ; number of bytes after LIB
UWORD LIB_VERSION ; major
UWORD LIB_REVISION ; minor
APTR LIB_IDSTRING ; ASCII identification
ULONG LIB_SUM ; the system-calculated checksum
UWORD LIB_OPENCNT ; number of current opens
LABEL LIB_SIZE ; Warning: Size is not a longword multiple!
</pre></div> </div> </div><p>The binary code of Kickstart 1.3 from address <code>0xfc030c</code> is indeed the following</p><div class="code"><div class="content"><div class="highlight"><pre>0000030c: 09 ; LN_TYPE
0000030d: 00 ; LN_PRI
0000030e: 00fc 00a8 ; LN_NAME
00000312: 06 ; LIB_FLAGS
00000313: 00 ; LIB_pad
00000314: 0000 ; LIB_NEGSIZE
00000316: 024c ; LIB_POSSIZE
00000318: 0022 ; LIB_VERSION
0000031a: 0002 ; LIB_REVISION
0000031c: 00fc 0018 ; LIB_IDSTRING
00000320: 0000 0000 ; LIB_SUM
00000324: 0001 ; LIB_OPENCNT
</pre></div> </div> </div><p>From this I know that the version of <code>exec</code> contained in this Kickstart is 34 (<code>0x22</code>) revision 2 (<code>0x02</code>), and this is confirmed by the ID string at address <code>0xfc0018</code>, which is <code>exec 34.2 (28 Oct 1987)</code>.</p><div class="imageblock"><img src="/images/exploring-the-amiga-3/exec-version-string.png"><div class="title">Exec version string</div></div><h3 id="step-3-1ec4">Step 3</h3><p>What we are really interested in, at this point, is where the address of this structure is mentioned in the code, as it will be used to create the library structure. Since the <code>MakeFunctions</code> routine will be invoked after creating the library structure, we can know from here where the former is defined.</p><p>The structure is at address <code>0x030c</code> and we are looking for and instruction like <code>lea 0x30c(pc),ax</code>, where <code>ax</code> is one of the address registers <code>a0</code>-<code>a7</code>. Loading the address of a table in a register is the standard way to loop on the table to modify it or to copy the bytes somewhere. It was interesting to discover why this is the preferred way to do it</p><div class="callout"><div class="content"><p>The 68000 does not allow you to execute a MOVE instruction with a destination relative to the program counter (PC). In the view of the 68000 designers, code should not patch itself. If you must change a table in the middle of code, you must point to it with an instruction like LEA TABLE(PC),An and then alter it through An. (Self-modifying code is especially bad for 68000 programs that may someday run on the 68020, because the 68020's instruction cache normally assumes that code is pure.</p>
<p>(from <a href="http://www.easy68k.com/paulrsm/doc/trick68k.htm">http://www.easy68k.com/paulrsm/doc/trick68k.htm</a>)</p></div></div><p>At address <code>0x0364</code> we find the following code</p><div class="code"><div class="content"><div class="highlight"><pre>00000360: 43ee 0008 lea 0x8(a6),a1
00000364: 41fa ffa6 lea 0x30c(pc),a0
00000368: 700c moveq #0xc,d0
0000036a: 32d8 move.w (a0)+,(a1)+
0000036c: 51c8 fffc dbf d0,0x36a
</pre></div> </div> </div><p>which actually installs in memory the exec library. Let's analyse this code instruction by instruction.</p><p>Since the ExecBase address is contained in <code>a6</code> (this is done previously in the code), that address is incremented by 8 and the result is copied into the <code>a1</code> register. The 8 bytes leave space for the <code>LN_SUCC</code> and <code>LN_PRED</code> pointers. Then, the code loads the address of the table in <code>a0</code>. </p><p>The loop is performed on 26 bytes. The number 12 (<code>0xc</code>) is copied into <code>d0</code>, but the instruction <code>dbf</code> (<code>dbra</code> in some assemblers) keeps jumping to <code>0x36a</code> until the value of <code>d0</code> is -1, so it is actually performing the loop code 13 times. Since the <code>move.w</code> instruction moves words we are copying 26 bytes, which is exactly the size of the library node from <code>LN_TYPE</code> to <code>LIB_OPENCNT</code> included.</p><p>The next 5 instructions are</p><div class="code"><div class="content"><div class="highlight"><pre>00000370: 204e movea.l a6,a0
00000372: 43fa 1708 lea 0x1a7c(pc),a1
00000376: 2449 movea.l a1,a2
00000378: 6100 1238 bsr.w 0x15b2
0000037c: 3d40 0010 move.w d0,0x10(a6)
</pre></div> </div> </div><p>From the <a href="http://amigadev.elowar.com/read/ADCD_2.1/Includes_and_Autodocs_3._guide/node021A.html">documentation</a> we know that <code>MakeFunctions</code> has the following prototype</p><div class="code"><div class="content"><div class="highlight"><pre>size = MakeFunctions(address, vectors, offset)
d0 a0 a1 a2
</pre></div> </div> </div><p>where <code>address</code> is the address where the jump table will be constructed, <code>vectors</code> is a table that lists the function addresses (the one we are looking for) and <code>offset</code> tells the function if the function addresses are absolute (value is <code>0</code>) or relative (in which case offset is the base for the displacement). The list of addresses has to be terminated with -1 (<code>0xffff</code>).</p><p>So the first line stores in <code>a0</code> the content of <code>a6</code>, which is the ExecBase address. This is where we want to install the library. The second line loads the address of the vectors table in <code>a1</code> and the same value is stored in <code>a2</code>. Then the code branches to the subroutine at <code>0x15b2</code> which at this point we know is the address of <code>MakeFunctions</code>.</p><h3 id="step-4-7488">Step 4</h3><p>We extracted two useful information from this code. First, the vector table is at address <code>0x1a7c</code>, and second the <code>MakeFunctions</code> subroutine is at address <code>0x15b2</code>. The latter will be useful to double check the content of the vector table.</p><p>After <code>MakeFunctions</code> has been executed, the code returns and the next instruction stores the final size of the jump table 16 bytes after the address contained in <code>a6</code>. With the help of the structures shown above we know that at that offset we can find the <code>LIB_NEGSIZE</code> field, that contains the size of the jump table (number of bytes before the library).</p><p>It's time to double-check what we found. There should be a table at address <code>0x1a7c</code> that contains function addresses in the order listed by the include file <code>include_i/exec/exec_lib.i</code>. As <code>MakeFunctions</code> itself is listed in that file at the 11th place we can check if the table is consistent. That address should point a function at <code>0x15b2</code>, according to the previous code.</p><p>The values at <code>0x1a7c</code> are the following </p><div class="code"><div class="content"><div class="highlight"><pre>00001a7c: 08a0
00001a7e: 08a8
00001a80: 08ac
00001a82: 08ac
00001a84: ee6a
00001a86: f420
00001a88: f446
00001a8a: 04f8
00001a8c: f4a0
00001a8e: f4ea
00001a90: f58e
00001a92: f0b0
00001a94: f188
00001a96: faac
00001a98: fb36
00001a9a: f080
; ...
</pre></div> </div> </div><p>The file <code>include_i/exec/exec_lib.i</code> doesn't contain the first 4 reserved vectors (the functions <code>Open</code>, <code>Close</code>, <code>Expunge</code>, and the reserved space), so considering that those are in the vector table we should check the 15th, were we find <code>0xfb36</code>. This is an offset relative to the beginning of the table, so the function is at <code>0x1a7c + 0xfb36 = 0x15b2</code> (addresses are 16 bits numbers), as we already discovered.</p><p>This shows that our investigation is correct. The Kickstart 1.3 vector table is at address <code>0x1a7c</code> and from there we can reach and analyse all the functions contained in the base Amiga library.</p><h2 id="whats-next-0c7e">What's next<a class="headerlink" href="#whats-next-0c7e" title="Permanent link">¶</a></h2><p>In the next article I will show the code of the 4 Exec base functions and discuss <code>MakeFunctions</code> in depth. I will also briefly discuss self-modifying code, as <code>MakeFunctions</code> has a good example of it.</p><h2 id="resources-edc5">Resources<a class="headerlink" href="#resources-edc5" title="Permanent link">¶</a></h2><ul><li>Amiga System Programmers Guide, Abacus - <a href="https://archive.org/details/Amiga_System_Programmers_Guide_1988_Abacus">https://archive.org/details/Amiga_System_Programmers_Guide_1988_Abacus</a></li><li><a href="http://amigadev.elowar.com">AmigaOS Developer Docs</a></li><li>Amiga Forever package sold by Cloanto <a href="https://www.amigaforever.com">here</a></li></ul><h2 id="updates-0083">Updates<a class="headerlink" href="#updates-0083" title="Permanent link">¶</a></h2><p>2018-06-23: As <a href="https://new.reddit.com/user/Malor">Malor</a> pointed out on Reddit (<a href="https://new.reddit.com/r/programming/comments/8pkgk0/exploring_the_amiga_part_1/e0cifax">here</a>) there is no need to own the original hardware, as licenses are still sold by Cloanto. Thanks Malor!</p><h2 id="feedback-d845">Feedback<a class="headerlink" href="#feedback-d845" title="Permanent link">¶</a></h2><p>Feel free to reach me on <a href="https://twitter.com/thedigicat">Twitter</a> if you have questions. The <a href="https://github.com/TheDigitalCatOnline/blog_source/issues">GitHub issues</a> page is the best place to submit corrections.</p>Exploring the Amiga - Part 22018-05-28T15:00:00+01:002021-12-21T08:00:00+00:00Leonardo Giordanitag:www.thedigitalcatonline.com,2018-05-28:/blog/2018/05/28/exploring-the-amiga-2/<p>The jump table of libraries in the Amiga system, types and structures of Kickstart 1.3</p><h2 id="the-library-jump-table-d4b7">The library jump table<a class="headerlink" href="#the-library-jump-table-d4b7" title="Permanent link">¶</a></h2><p>As already mentioned when a library is loaded in memory a jump table is created just before the library base address. This table contains the addresses of the functions exposed by the library, and Exec itself has one.</p><p>The jump table functions order for the Exec library is specified in one of the include files provided by the NDK, namely <code>include_i/exec/exec_lib.i</code>.</p><div class="code"><div class="content"><div class="highlight"><pre> FUNCDEF Supervisor
FUNCDEF execPrivate1
FUNCDEF execPrivate2
FUNCDEF execPrivate3
...
FUNCDEF OpenLibrary
...
</pre></div> </div> </div><p>As you can see this file makes use of the <code>FUNCDEF</code> macro, which is not provided and has to be implemented by the coder. The idea of the macro is very simple: as the order of the jump table does not change we can just replace the first <code>FUNCDEF</code> with the offset of the first function in the library and then increment this offset with the default size of the jump address. The expected output of the macro is</p><div class="code"><div class="content"><div class="highlight"><pre> _LVOSupervisor EQU -30
_LVOexecPrivate1 EQU -36
_LVOexecPrivate2 EQU -42
_LVOexecPrivate3 EQU -48
...
_LVOOpenLibrary EQU -552
...
</pre></div> </div> </div><p>Please note that the name of the function has been replaced by another string prepending <code>_LVO</code> to avoid clashes with the actual function definition (LVO stands for Library Vector Offset).</p><p>The above figures come from the "Special Constants" section contained in the <code>include_i/exec/libraries.i</code> file</p><div class="code"><div class="content"><div class="highlight"><pre>*------ Special Constants ---------------------------------------
LIB_VECTSIZE EQU 6 ;Each library entry takes 6 bytes
LIB_RESERVED EQU 4 ;Exec reserves the first 4 vectors
LIB_BASE EQU -LIB_VECTSIZE
LIB_USERDEF EQU LIB_BASE-(LIB_RESERVED*LIB_VECTSIZE) ;First user func
LIB_NONSTD EQU LIB_USERDEF
</pre></div> </div> </div><p>AS you can see from the comments, Exec reserves the first 4 vectors, so the first function's address is <code>LIB_USERDEF</code>. To understand why the addresses are negative and how the offset is computed let's get a snapshot of the library once it has been loaded in memory</p><div class="code"><div class="content"><div class="highlight"><pre> HIGHER MEMORY ADDRESSES
+-------------------------+
Last byte of the | End of the library |
library loaded in ---------> +-------------------------+
memory | [...] |
+-------------------------+
| Content of the library |
+-------------------------+
| Library structure |
Library base address ------> +-------------------------+
| 1st reserved vector |
+-------------------------+ <--- LIB_BASE
| 2nd reserved vector |
+-------------------------+ <--+
| 3rd reserved vector | | LIB_VECTSIZE
+-------------------------+ <--+
| 4th reserved vector |
+-------------------------+
| 1st defined function |
+-------------------------+ <--- LIB_USERDEF
| 2nd defined function |
+-------------------------+
| [...] |
+-------------------------+
First byte of the | End of the jump table |
library loaded in ---------> +-------------------------+
memory LOWER MEMORY ADDRESSES
</pre></div> </div> </div><p>You can find an official version of this in the <a href="http://amigadev.elowar.com/read/ADCD_2.1/AmigaMail_Vol2_guide/node0189.html">documentation </a>. Pay attention that the picture in the documentation represents memory upside down, with lower memory addresses towards the top of the page.</p><p>As you can see the library is loaded as expected from the base address towards the higher memory addresses, but at the same time the jump table is prefixed <em>in reverse order</em>. This is done to allow you to find the address of a function with a simple (negative) indexing instead of a more complex algorithm. Function number 1 is at address <code>-1 * address_size</code>, function number 2 at address <code>-2 * address_size</code>, etc.</p><p>This is why we use negative offsets to call library functions but positive ones to access the library data and structures.</p><p>You can also see from the figure where the Special Constants <code>LIB_BASE</code> and <code>LIB_USERDEF</code> are located. The actual values are</p><div class="code"><div class="content"><div class="highlight"><pre>LIB_BASE EQU -6
LIB_USERDEF EQU -30
</pre></div> </div> </div><p>A good definition of the <code>FUNCDEF</code> macro, thus, is the following</p><div class="code"><div class="content"><div class="highlight"><pre> INCLUDE "exec/libraries.i"
MACRO FUNCDEF
_LVO\1 EQU FUNC_CNT
FUNC_CNT SET FUNC_CNT-LIB_VECTSIZE
ENDM
FUNC_CNT SET LIB_USERDEF
</pre></div> </div> </div><p>The last line initialises the <code>FUNC_CNT</code> symbol with the <code>LIB_USERDEF</code> value. Then each call of the <code>FUNCDEF <arg></code> macro does two things:</p><ol><li>Creates the <code>_LVO<arg></code> symbol with value <code>FUNC_CNT</code> (e.g. <code>_LVOSupervisor EQU -30</code>)</li><li>Decrements the <code>FUNC_CNT</code> symbol by <code>LIB_VECTSIZE</code></li></ol><p>Please note that the example <code>FUNCDEF</code> that you can find (commented) in <code>libraries.i</code> won't work out of the box as <code>FUNC_CNT</code> is defined inside the macro itself, while it has to be already defined before the first use of the macro.</p><div class="code"><div class="content"><div class="highlight"><pre>*------ FUNCDEF is used to parse library offset tables. Many applications
*------ need a special version of FUNCDEF - you provide your own macro
*------ to match your needs. Here is an example:
*
* FUNCDEF MACRO
* _LVO\1 EQU FUNC_CNT
* FUNC_CNT SET FUNC_CNT-6 * Standard offset-6 bytes each
* FUNC_CNT EQU LIB_USERDEF * Skip 4 standard vectors
* ENDM
</pre></div> </div> </div><p>You can put the <code>FUNCDEF</code> macro code in a local include file like <code>funcdef.i</code>. Including it your code allows you to use <code>_LVO</code> prefixed labels for the functions that you want to load</p><div class="code"><div class="content"><div class="highlight"><pre> INCLUDE "funcdef.i"
INCLUDE "exec/exec_lib.i"
move.l 4.w,a6
clr.l d0
move.l #libname,a1
jsr _LVOOpenLibrary(a6)
libname:
dc.b "somename.library",0
</pre></div> </div> </div><p>Finally, if you want to be even more explicit you can use the <code>CALLLIB</code> macro defined in <code>libraries.i</code> and write</p><div class="code"><div class="content"><div class="highlight"><pre> INCLUDE "funcdef.i"
INCLUDE "exec/exec_lib.i"
INCLUDE "exec/libraries.i"
move.l 4.w,a6
clr.l d0
move.l #libname,a1
CALLLIB _LVOOpenLibrary
libname:
dc.b "somename.library",0
</pre></div> </div> </div><h2 id="the-four-reserved-vectors-6f35">The four reserved vectors<a class="headerlink" href="#the-four-reserved-vectors-6f35" title="Permanent link">¶</a></h2><p>As we saw, the Amiga system reserves 4 vectors at the beginning of the jump table of a library. These 4 spaces host 3 standard functions that shall be provided by any library, <code>Open()</code>, <code>Close()</code>, and <code>Expunge()</code>. The fourth slot is kept for possible future expansions and must contain a function that returns 0.</p><p>The offsets of these functions are contained in the <code>include_i/exec/libraries.i</code> file</p><div class="code"><div class="content"><div class="highlight"><pre>*----------------------------------------------------------------
*
* Standard Library Functions
*
*----------------------------------------------------------------
LIBINIT LIB_BASE
LIBDEF LIB_OPEN
LIBDEF LIB_CLOSE
LIBDEF LIB_EXPUNGE ; must exist in all libraries
LIBDEF LIB_EXTFUNC ; for future expansion - must return zero.
</pre></div> </div> </div><p>the effect of the above macros with the previous constants is</p><div class="code"><div class="content"><div class="highlight"><pre>LIB_OPEN EQU -6
LIB_CLOSE EQU -12
LIB_EXPUNGE EQU -18
LIB_EXTFUNC EQU -24
</pre></div> </div> </div><p>You can try to follow the definitions of the <code>LIBINIT</code> and <code>LIBDEF</code> macros to obtain the same result.</p><h2 id="types-and-structures-244a">Types and structures<a class="headerlink" href="#types-and-structures-244a" title="Permanent link">¶</a></h2><p>Let's see how the Exec library defines its types, which are the base components of the Amiga system. The main entry point for this investigation is the <code>include_i/exec/types.i</code> file.</p><p>When working with data structures in Assembly, everything is expressed in terms of offsets. The main idea behind structures is to create something like this</p><div class="code"><div class="content"><div class="highlight"><pre>STRUCT1 EQU 0
OFFS SET 0
FIELD1 EQU OFFS
OFFS EQU OFFS+SIZE_OF_FIELD1
FIELD2 EQU OFFS
OFFS EQU OFFS+SIZE_OF_FIELD2
; ...
STRUCT1_SIZE EQU OFFS
</pre></div> </div> </div><p>which, once run through the macro expansion, creates the following code</p><div class="code"><div class="content"><div class="highlight"><pre>STRUCT1 EQU 0
FIELD1 EQU 0
FIELD2 EQU SIZE_OF_FIELD1
FIIELD3 EQU SIZE_OF_FIELD1+SIZE_OF_FIELD2
; ...
STRUCT1_SIZE EQU SIZE_OF_FIELD1+...+SIZE_OF_FIELDn
</pre></div> </div> </div><p>So, the type macros are all defined with code like this</p><div class="code"><div class="content"><div class="highlight"><pre>TYPENAME MACRO
\1 EQU SOFFSET
SOFFSET SET SOFFSET+SIZE_OF_TYPE
ENDM
</pre></div> </div> </div><p>For example the <code>BYTE</code> macro is</p><div class="code"><div class="content"><div class="highlight"><pre>BYTE MACRO ; byte (8 bits)
\1 EQU SOFFSET
SOFFSET SET SOFFSET+1
ENDM
</pre></div> </div> </div><p>Note that the field is defined with <code>EQU</code> to avoid unwanted overwrites, while <code>SOFFSET</code> uses <code>SET</code> that allows to redefine the symbol.</p><p>Let's see now how a real structure is defined. A good example is <code>LN</code> defined in <code>include_i/exec/nodes.i</code> which represents a node of a linked list.</p><div class="code"><div class="content"><div class="highlight"><pre> STRUCTURE LN,0 ; List Node
APTR LN_SUCC ; Pointer to next (successor)
APTR LN_PRED ; Pointer to previous (predecessor)
UBYTE LN_TYPE
BYTE LN_PRI ; Priority, for sorting
APTR LN_NAME ; ID string, null terminated
LABEL LN_SIZE ; Note: word aligned
</pre></div> </div> </div><p>The <code>STRUCTURE</code> macro is defined in <code>types.i</code> as</p><div class="code"><div class="content"><div class="highlight"><pre>STRUCTURE MACRO ; structure name, initial offset
\1 EQU 0
SOFFSET SET \2
ENDM
</pre></div> </div> </div><p>And the resulting declarations, once the macros have been expanded, are the following</p><div class="code"><div class="content"><div class="highlight"><pre>LN EQU 0
LN_SUCC EQU 0
LN_PRED EQU 4
LN_TYPE EQU 8
LN_PRI EQU 9
LN_NAME EQU 10
LN_SIZE EQU 14
</pre></div> </div> </div><p>As you can see the field names are just offsets inside the structure, and there is no specific padding at the end to align the structure. In this case there is no need, as the structure size is already a multiple of a word (14 bytes).</p><h3 id="how-to-align-structures-98d7">How to align structures</h3><p>If we need to align the bytes however we can use a little binary trick. If you ignore the least significant bit of a binary number you convert it to the nearest even number (downwards). An example in Python is</p><div class="code"><div class="content"><div class="highlight"><pre><span class="o">>>></span> <span class="nb">bin</span><span class="p">(</span><span class="mi">13</span><span class="p">)</span>
<span class="o">>>></span> <span class="s1">'0b1101'</span>
<span class="o">>>></span> <span class="nb">bin</span><span class="p">(</span><span class="mi">12</span><span class="p">)</span>
<span class="o">>>></span> <span class="s1">'0b1100'</span>
</pre></div> </div> </div><p>You can ignore the least significant bits with a simple bitwise AND</p><div class="code"><div class="content"><div class="highlight"><pre><span class="o">>>></span> <span class="nb">bin</span><span class="p">(</span><span class="mi">14</span><span class="p">)</span>
<span class="s1">'0b1110'</span>
<span class="o">>>></span> <span class="mi">14</span><span class="o">&</span><span class="mb">0b1100</span>
<span class="mi">12</span>
</pre></div> </div> </div><p>So, given the current offset, if we increase it by one and round down to the nearest integer we are aligning the offset to multiples of a word (2 bytes). The <code>ALIGNWORD</code> macro in the <code>include_i/exec/types.i</code> file implements exactly this algorithm</p><div class="code"><div class="content"><div class="highlight"><pre>ALIGNWORD MACRO ; Align structure offset to nearest word
SOFFSET SET (SOFFSET+1)&$fffffffe
ENDM
</pre></div> </div> </div><p>This can be seen in action in the <code>CardHandle</code> structure defined in <code>include_i/resources/card.i</code>. The same algorithm is implemented in other parts of the Kickstart code, for example in the <code>AddMemList</code> function that adds memory space to the free memory pool.</p><h2 id="markus-wandels-work-048e">Markus Wandel's work<a class="headerlink" href="#markus-wandels-work-048e" title="Permanent link">¶</a></h2><p>After three years since I began this investigation I came across the work of Marcus Wandel (<a href="http://wandel.ca">http://wandel.ca</a>), advertised on <a href="http://amigan.1emu.net/aw/TransactorUK-sep89.pdf">Amiga Transactor September 1989</a>. Mr Wandel disassembled the whole Kickstart 1.2 ROM back in 1989, and you can find the result of his effort on his website at <a href="http://wandel.ca/homepage/execdis/index.html">http://wandel.ca/homepage/execdis/index.html</a>. I'm currently using his comments to check what I find out on my own, because I don't want to spoil the joy of discovery, and so far they confirmed I'm on the right path.</p><p>I think what Mr Wandel did is a great example of what computer science truly is. I'm sure his impressive contribution helped people to understand the Amiga system back in the ages, and it's definitely helping me 32 years later. Without the effort and the passion of people like Marcus Wandel, computer science would be just another corporate-owned environment.</p><p>Marcus's name is not as famous as that of other big players, but I consider his work extremely important. Thanks Marcus for your work and thanks to all the people who contributed to the Amiga, and to the rise of microcomputers.</p><h2 id="whats-next-0c7e">What's next<a class="headerlink" href="#whats-next-0c7e" title="Permanent link">¶</a></h2><p>The next article will describe in depth how the jump table of the Exec library is created through the <code>MakeFunctions</code> routine. This will be shown step by step discussing the reverse engineering method followed to discover the mechanism and the relevant code.</p><h2 id="resources-edc5">Resources<a class="headerlink" href="#resources-edc5" title="Permanent link">¶</a></h2><ul><li>Amiga System Programmers Guide, Abacus - <a href="https://archive.org/details/Amiga_System_Programmers_Guide_1988_Abacus">https://archive.org/details/Amiga_System_Programmers_Guide_1988_Abacus</a></li><li><a href="http://amigadev.elowar.com">AmigaOS Developer Docs</a></li><li><a href="http://wandel.ca/homepage/execdis/index.html">Marcus Wandel's disassmbly of Kickstart 1.2</a></li></ul><h2 id="feedback-d845">Feedback<a class="headerlink" href="#feedback-d845" title="Permanent link">¶</a></h2><p>Feel free to reach me on <a href="https://twitter.com/thedigicat">Twitter</a> if you have questions. The <a href="https://github.com/TheDigitalCatOnline/blog_source/issues">GitHub issues</a> page is the best place to submit corrections.</p>Exploring the Amiga - Part 12018-05-28T14:00:00+01:002021-02-26T08:00:00+00:00Leonardo Giordanitag:www.thedigitalcatonline.com,2018-05-28:/blog/2018/05/28/exploring-the-amiga-1/<p>How to disassemble Amiga binaries and a discussion of the LEA instruction and relative offsets</p><p>Recently I decided to give some time to retroprogramming, and in particular to explore the architecture of some famous microcomputer and consoles of the 80s and 90s. Glorious 8-bit systems like the ZX Spectrum, the Commodore 64, or the Nintendo Entertainment System. 16-bits giants the likes of the Amiga, its (eventually winning) MS-DOS-based counterparts running on the early x86 architectures, the immortal Nintendo Super Famicom and SEGA Genesis.</p><p>I am deeply convinced that learning architectures is a perfect way to become a better programmer, even now that we have 64-bits processors, fifth generation languages, and Internet. Those old systems had many limitations and to get interesting results the programmer has to know the hardware and exploit every single bit of power it can deliver.</p><p>This is sometimes not true any more nowadays. On the one hand this is good, as it allows us to concentrate on business decisions and on higher layer of abstraction. On the other hand it was and it will always be useful for a programmer to face a limited system and to try to get the maximum out of it.</p><p>This series of posts is about the Commodore Amiga. Thousands of words have already been written on the Amiga, and I will not add anything but "milestone" to the adjectives used to describe it. This post and the following ones are not intended to be a complete and well-organised review of the architecture. Instead, they will be more a set of "lab notes" for myself that I write while I explore the platform. I put them on the blog in the hope that they will be useful for other programmers that try to crack the same problems.</p><h2 id="assembly-language-and-the-amiga-5bc5">Assembly language and the Amiga<a class="headerlink" href="#assembly-language-and-the-amiga-5bc5" title="Permanent link">¶</a></h2><p>If you want to write Assembly programs for the Amiga you can either work directly on a real system or use a cross-compiler. I prefer to work on my Linux system because, as much as I like retro architectures, I also like the power of a good Unix system and a modern editor.</p><p>Cross-compiling is a very simple concept: instead of compiling source code and creating binaries for the architecture you are running the compiler on, you create binaries for a different architecture. In this case the host architecture is Linux/amd64 and the target architecture is Amiga.</p><p>As this is not the only project I am following at the moment, I created a directory to host everything I need for the Amiga development: compiler, documentation, scripts.</p><h3 id="install-vasm-159b">Install vasm</h3><p>On Linux you can both use the GCC compiler or install vasm. The latter uses the same syntax as the standard Amiga assemblers, especially for compiler directives like <code>macro</code> and <code>include</code>, thus making it easier to pick up and use code published in books and in magazines during the 80s.</p><p>To install the latest vasm you can run the following code</p><div class="code"><div class="content"><div class="highlight"><pre>wget -q "http://sun.hasenbraten.de/vasm/release/vasm.tar.gz"
tar xvf vasm.tar.gz # The file is not actually gzipped
rm vasm.tar.gz
cd vasm
make CPU=m68k SYNTAX=mot
cd ..
</pre></div> </div> </div><h3 id="install-a-disassembler-d73b">Install a disassembler</h3><p>The vbcc suite written by Volker Barthelmann contains a disassembler for the M68k architecture that you can find <a href="http://sun.hasenbraten.de/~frank/projects/">here</a>.</p><p>A disassembler is a handy tool that can reveal a lot about how a program works. You can install it with the following code</p><div class="code"><div class="content"><div class="highlight"><pre>wget -q "http://sun.hasenbraten.de/~frank/projects/download/vdam68k.tar.gz"
tar xvzf vdam68k.tar.gz
cd vda/M68k/
make
</pre></div> </div> </div><p>which will create the executable <code>vda/M68k/vda68k</code> that you can use to disassemble Amiga programs or ROM dumps.</p><h3 id="install-the-ndk-2146">Install the NDK</h3><p>The Native Development Kit (the Amiga SDK) contains include files that can be very helpful (though not strictly necessary) when developing. Unfortunately the NDK is still copyrighted by some of the guys that are trying to resurrect the Amiga. The latter idea is nice, but I really do not understand how preventing distribution of development documentation about a platform dead more than 20 years ago might help such a project.</p><p>Whatever, you can get an old version of the Amiga Developer CD <a href="https://archive.org/details/amiga-developer-cd-v1_1">here</a>. This contains the NDK version 3.1 which is enough for what we are going to learn in this series.</p><p>You may want to rename the <code>Includes&Libs</code> directory to <code>Includes_Libs</code> to simplify its access by the compiler command line (<code>&</code> is a special character in bash).</p><h3 id="helper-script-and-test-2ce9">Helper script and test</h3><p>I created a <code>asm.sh</code> helper script to simplify the development process</p><div class="code"><div class="content"><div class="highlight"><pre><span class="ch">#!/bin/bash</span>
<span class="nv">BASE</span><span class="o">=</span><span class="s2">"/where/you/put/everything"</span>
<span class="si">${</span><span class="nv">BASE</span><span class="si">}</span>/vasm/vasmm68k_mot<span class="w"> </span>-kick1hunks<span class="w"> </span>-Fhunkexe<span class="w"> </span>-I<span class="si">${</span><span class="nv">BASE</span><span class="si">}</span>/NDK_3.1/Include_Libs/include_i<span class="w"> </span>-o<span class="w"> </span><span class="si">${</span><span class="nv">1</span><span class="p">/.asm/</span><span class="si">}</span><span class="w"> </span>-nosym<span class="w"> </span><span class="nv">$1</span>
</pre></div> </div> </div><p>Don't forget to run <code>chmod 775 asm.sh</code> to make the script executable. Now run the following command</p><div class="code"><div class="content"><div class="highlight"><pre><span class="nb">echo</span><span class="w"> </span>-e<span class="w"> </span><span class="s2">"loop:\n btst #6,\$bfe001\n bne loop\n rts\n"</span><span class="w"> </span>><span class="w"> </span>test.asm<span class="w"> </span><span class="o">&&</span><span class="w"> </span>./asm.sh<span class="w"> </span>test.asm
</pre></div> </div> </div><p>This compiles a very small program that loops until you press the right mouse button. If everything has been correctly installed you should get the following output</p><div class="code"><div class="content"><div class="highlight"><pre>vasm 1.8c (c) in 2002-2018 Volker Barthelmann
vasm M68k/CPU32/ColdFire cpu backend 2.3b (c) 2002-2017 Frank Wille
vasm motorola syntax module 3.11c (c) 2002-2018 Frank Wille
vasm hunk format output module 2.9b (c) 2002-2017 Frank Wille
CODE(acrx2): 12 bytes
</pre></div> </div> </div><p>And running the <code>file</code> command should return the correct type</p><div class="code"><div class="content"><div class="highlight"><pre>$ file test
test: AmigaOS loadseg()ble executable/binary
</pre></div> </div> </div><h2 id="lea-the-pc-and-the-relative-offset-b4f8">LEA, the PC and the relative offset<a class="headerlink" href="#lea-the-pc-and-the-relative-offset-b4f8" title="Permanent link">¶</a></h2><p>In Assembly, you can manage memory cells using either their content or the address, as you can do with pointers in C or similar concepts in other high-level languages. You can also set labels that the assembler will convert into instruction addresses, but these are (almost) always relative to the beginning of the code itself, as the code may be loaded anywhere in memory.</p><p>This means that, generally speaking, all the addresses we use when we branch to other parts of the code should be relative to the current instruction.</p><p>The Motorola 68k calls this addressing mode <em>Program Counter Indirect with Displacement Mode</em>. Its description contained in the Programmer's Reference Manual is</p><div class="callout"><div class="content"><p>In this mode, the operand is in memory. The address of the operand is the sum of the address in the program counter (PC) and the sign-extended 16-bit displacement integer in the extension word. The value in the PC is the address of the extension word.</p>
<p>(2.2.11, page 2-13)</p></div></div><p>The usual assembler syntax for this addressing mode is <code>(d16,PC)</code> or <code>d16(PC)</code>, where <code>d16</code> is a 16-bits displacement. The <code>lea</code> instruction, for example, supports this mode, so we can find code like</p><div class="code"><div class="content"><div class="highlight"><pre>00000364: 41fa ffa6 lea 0x30c(pc),a0
</pre></div> </div> </div><p>Here the instruction is stored at address <code>0x0364</code> and it loads in the <code>a0</code> register the <em>effective address</em> of the instruction at <code>0x030c</code>.</p><p>Pay attention: since your code starts always at address <code>0x0</code>, you might be tempted to store the value <code>0x030c</code> into <code>a0</code> and go on. You want however to load the address that instruction has at runtime, which will be different from the current one. The displacement, however, will be the same, as the code doesn't change its form, and this is why this addressing mode is useful.</p><h3 id="relative-offset-encoding-455d">Relative offset encoding</h3><p>A question may arise, then: why does the code show the address <code>0x30c</code>, which is the effective address, if the displacement is relative?</p><p>In the example, the syntax <code>0x30c(pc)</code> doesn't mean "the line at <code>0x30c</code> from the current line", but "the line at <code>0x30c</code> <strong>considering that</strong> the current line is <code>0x0364</code>". Let's dig into the binary representation of the instruction to see how the processor receives it. The value <code>0x41faffa6</code> in binary form is </p><div class="code"><div class="content"><div class="highlight"><pre>01000001111110101111111110100110
</pre></div> </div> </div><p>If we split it according to the Motorola 68k opcodes scheme (Programmer's Reference Manual, Section 8) we get</p><div class="code"><div class="content"><div class="highlight"><pre>0100 000 111 111010 1111111110100110
^ ^ ^
lea a0 (d16,PC)
</pre></div> </div> </div><p>So we know this is a <code>lea</code> to <code>a0</code> using Program Counter with Displacement. The address argument is <code>1111111110100110</code>, which has to be interpreted as a "sign-extended 16-bit displacement integer" as the Reference Manual stated. The value is the two's complement representation of the decimal <code>-90</code>, and since the PC is pointing at the address itself (<code>0x0366</code>) the resulting address is <code>0x0366 - 90</code>, which gives <code>0x030c</code>.</p><p>The fact that the PC is pointing at the address might be overlooked. The manual says</p><div class="callout"><div class="content"><p>The value in the PC is the address of the extension word. (2.2.11, page 2-13)</p></div></div><p>So, while the Assembly code uses the absolute value, the actual opcode contains a true displacement from the current position. The disassembler formats the value so that it is easy for us to understand the effective address (<code>0x30c</code>), but also telling us that there is more going on behind the scenes using the <code>(pc)</code>suffix.</p><h2 id="how-to-open-a-library-c545">How to open a library<a class="headerlink" href="#how-to-open-a-library-c545" title="Permanent link">¶</a></h2><p>When you code in a high level language like C you usually refer to functions of an external library in your code and then provide the library object files on the compiler command line. The code of the library is either included in the code of your program or loaded into memory at runtime (shared libraries), but in both cases the function call is, at the machine language level, just a jump to a different address in memory.</p><p>If you write a program directly in Assembly language things are not different, you can always rely on the linker to properly address external libraries. In a platform like Amiga, however, it's custom to access the system libraries in a direct way, manually jumping to the right address, which is why sometimes tutorials and books contain "magic numbers".</p><p>When the Amiga OS loads a library in memory the Exec master library analyses its structure and creates the so-called <em>jump table</em>. This is nothing more than an array that lists the addresses of the functions exposed by the library. This is a very simple and effective way to let the OS free to load the library anywhere in memory (relocation).</p><p>The Exec master library is not different, but this library is loaded as part of the bootstrap process, and the base address is always stored in memory location <code>0x4</code>. To use one of Exec's functions, then, we just need to issue a <code>jsr <address></code> (Jump to SubRoutine), where <code><address></code> is the current position in memory of the function we want to call. Since we don't know the absolute address, being the library dynamically loaded, we use the library's jump table to retrieve the base address and get the function address as a fixed offset from the former.</p><p>Many Amiga programmers knew (and know) the addresses by heart, which is fine since the Amiga OS promises not to change the order of the jump table among different versions of Exec. So, for example, the address of the <code>OpenLibrary</code> function can be found at <code>-552</code> bytes before the library base address, while <code>CloseLibrary</code> is at <code>-414</code>. To call the <code>OpenLibrary</code> function, then, you need the following code</p><div class="code"><div class="content"><div class="highlight"><pre> move.l 4.w,a6 ; a6 = base address of Exec
jsr -552(a6) ; OpenLibrary()
</pre></div> </div> </div><p>The first instruction moves the value contained at address <code>0x4</code> into the <code>a6</code> register. This way the register will contain the base address of Exec. Then it jumps to the subroutine which address is 552 bytes before that base address. So, if <code>a6</code> contains an address like <code>0x20000</code> the code jumps to <code>0x1fdd8</code> (<code>0x20000 - 552</code>). This also shows us that the jump table is actually a proper list of jump instructionss, not just addressess.</p><p>The <code>OpenLibrary</code> function, however, expects some parameters, as you can see on the documentation page <code>exec.library/OpenLibrary</code> (<a href="http://amigadev.elowar.com/read/ADCD_2.1/Includes_and_Autodocs_3._guide/node0222.html">here</a>).</p><div class="code"><div class="content"><div class="highlight"><pre>library = OpenLibrary(libName, version)
D0 A1 D0
</pre></div> </div> </div><p>The pointer to the library name has to be in the register <code>a1</code> and the minimum accepted version in <code>d0</code>. The code becomes</p><div class="code"><div class="content"><div class="highlight"><pre> move.l 4.w,a6 ; a6 = base address of Exec
clr.l d0 ; 0 = accept all versions
move.l #libname,a1 ; a1 = address of the libname string
jsr -552(a6) ; OpenLibrary()
libname:
dc.b "somename.library",0
</pre></div> </div> </div><h2 id="whats-next-0c7e">What's next<a class="headerlink" href="#whats-next-0c7e" title="Permanent link">¶</a></h2><p>In the next article I will explore the library jump table in detail, discussing in particular the first four entries. I will also show how types and structures are defined in the Assembly include files.</p><h2 id="resources-edc5">Resources<a class="headerlink" href="#resources-edc5" title="Permanent link">¶</a></h2><ul><li>Motorola M68000 Family Programmer's Reference Manual - <a href="https://www.nxp.com/docs/en/reference-manual/M68000PRM.pdf">https://www.nxp.com/docs/en/reference-manual/M68000PRM.pdf</a></li><li><a href="http://amigadev.elowar.com">AmigaOS Developer Docs</a></li></ul><h2 id="feedback-d845">Feedback<a class="headerlink" href="#feedback-d845" title="Permanent link">¶</a></h2><p>Feel free to reach me on <a href="https://twitter.com/thedigicat">Twitter</a> if you have questions. The <a href="https://github.com/TheDigitalCatOnline/blog_source/issues">GitHub issues</a> page is the best place to submit corrections.</p>