Amiga Machine Code Letter I - Deep Dive
Letter 1 does not explain the program in much detail. It’s just meant as a small test to see if you can get the code up and running and also explain what an assembler does. Let’s take a closer look at the program.
Btw. many details in the following, will be revealed in the other Amiga machine code letters, so it’s a bit early to go down this path.
The Seka can show the program translation to object code, line by line, by giving the option “v”.
Notice that the label “loop” is in the symbol table, with the value 10. This means that the loop in line 6 will be replaced with the value from the symbol table in the object code. If the test in line 5 turns out to be false, then the conditional jump (bne) in line 6 will jump to the address $1A, when 10 is subtracted from the program counter. This corresponds to line 5, and thus we have a loop!
The line by line view does not give us the correct view of the memory. Something is missing. E.g. line 1 in the picture above, shows the hex number ending on DF, but the source code ends on DFF09A. Where is the missing F09A?
To see the complete image of the memory, we can press “h” to get some stats about our program. Notice that the code starts at the address $C2847C. We can then use “q” to get a dump of the memory starting at the given address.
Let’s try to perform the translation to 68k binary code by hand, so that we can verify the hex dump above. There is a very good explaination of the 68k architecture and it’s addressing modes here
And we also need the 68k opcodes, i.e. the machine code representation in bytes. It looks somewhat complex and scary! 😕
The image above, shows the gritty details from the linked pdf file. I can imagine that there is some complexity to the 68k instruction decoder 😉
Let’s take a look at the program from letter one. Here shown togeather with it’s hex representation i.e. the output of the assembler. The “-” is used to divide the opcodes from the operands.
; file mc0101.S move.w #$4000, $DFF09A ; 33 FC - 40 00 - 00 DF F0 9A move.w #$03A0, $DFF096 ; 33 FC - 03 A0 - 0D FF 00 96 loop: move.w $DFF006, $DFF180 ; 33 F9 - 00 DF F0 06 - 00 DF F1 80 btst #6, $BFE001 ; 08 39 - 00 06 - 00 BF E0 01 bne.s loop ; 66 EC move.w #$83A0, $DFF096 ; 33 FC - 83 A0 - 00 DF F0 96 move.w #$C000, $DFF09A ; 33 FC - C0 00 - 00 DF F0 9A rts ; 4E 75
The first thing to notice is that each line of assembly code has a variable length binary representation. How the 68k instruction decoder handles the variable length, require us to go through some of the commands.
Inspired by the opcode pdf, I have made a table of addressing modes
and operation size
Let’s take a closer look at the first line in the code:
move.w #$4000, $DFF09A ; 33 FC - 40 00 - 00 DF F0 9A
The first two bytes is the opcode, followed by it’s two operands. The operands are not of the same size. One is two bytes and the other is four bytes. Let’s take a closer look at the opcode.
The size part indicates that a word (two bytes) are moved. From the mode and register fields, we see that the source is an immediate value, and the destination is an absolute long i.e. a memory address. A long is four bytes, or two words.
The opcode takes two bytes for the move, the first operand takes two bytes, and the second operand takes four bytes. That’s a total of eight bytes, which is exactly what the hex dump reveals!
Let’s move on to another move instruction, that does not use immediate values.
move.w $DFF006, $DFF180 ; 33 F9 - 00 DF F0 06 - 00 DF F1 80
The first two bytes is the move instruction indicator, followed by two four byte operands.
The size part reveals that we are moving a word. The mode and register fields indicates that the source and destination, both are absolute longs. We are moving the word value stored at address $DFF006 into the address at $DFF180.
Let’s continue with the bit test
btst #6, $BFE001 ; 08 39 - 00 06 - 00 BF E0 01
This one is interesting. If wee look $BFE001 up in the Amiga Hardware Reference Manual it says, that the left button on the Amiga mouse is connected to CIAAPRA ($BFE001). The button for port 1 is connected to bit 6. Our bit test checks if bit 6 is set, in other words if the left mouse button is pressed.
The bit test has different operating modes. One of them is the above, where we test a value in an address to see if bit 6 is set. When we test on an address, only a byte, 8 bits, can be tested. I will not go into details about the other mode - it must come in a later post. The mode and register field says that we are testing a value stored in an absolute long, i.e. the given address.
So, again we have a two byte opcode followed by a two byte value that we compare with, and then ends with a four byte oprand for the address to be tested. This is exactly also what the hex dump reveals.
In our configuration of the bit test opcode, we know that we can only test with a byte, so why are we using two bytes? The same argument goes for the second operand. Here we could just use three bytes, but have allocated four bytes.
I don’t know the precise answer yet, but I think it has something to do with word allignment in memory. Also notice that each assembler line starts on an even address. More on this in another post.
Let’s move on to the branch not equal opcode. This instruction tells us what to do, depending on the result of the previous bit test.
move.w $DFF006, $DFF180 ; 33 F9 - 00 DF F0 06 - 00 DF F1 80 btst #6, $BFE001 ; 08 39 - 00 06 - 00 BF E0 01 bne.s loop ; 66 EC
Here’s a closer look at the opcode.
The eight most significant bits reveal that we have a bne opcode, branch not equal, followed by a displacement. This displacement is in two’s complement, which is a way to indicate negative numbers in binary. The value $EC is -20 in decimal, and instructs the program counter to jumb 20 bytes backwards.
Imagine that the program counter is placed just after 66 EC. Then count 20 words backwards, and we reach the start of 33 F9. This is the loop! We are making the Amiga busy, checking if the left mouse button is pressed. If the mouse button is not pressed, the program will keep looping.
After the mouse button is pressed, the program will exit. The program counter will continue to run from the return address.
Voila! We now have a better understanding of what the program does. I also appreciate the help from the assembler in translating the machine code to the non trivial instruction set. The assembler does a great job of keeping the programmer sane, especially for a CISC microprocessor as the 68k.
Previous post: Amiga Machine Code Letter I
Next post: Amiga Machine Code Letter I - Debugger.