• Please review our updated Terms and Rules here

Need some help from 6809 programmers at byte level

Ruud

Veteran Member
Joined
Nov 30, 2009
Messages
1,401
Location
Heerlen, NL
Already in 1985 I started writing my own 6502 assembler, written in Turbo Pascal. And I have been expanding it with other CPUs. I'm busy assembling this MicroKit09 and want to be able to write software for it using my own assembler. That will be an weird experience because the last time I wrote in 6809 assembly was in 1986/1987 at HTS (Technical university ???). And even then, I wrote the program but didn't even see the binary result.

Anyway, I found another multi-CPU assembler written by A.C. Verschueren, also written in Pascal. I only found this about 20 years ago and he had a complete different approach of how to compile code. But the plus, I was able to change his code so far that it would accept my source code.

Please have a look at this code:
Code:
     0000 3404ABE0     label5: aba                     ; 6800 !
 ....
     0056 A99B         label3: adca    [d,x]           ; indirect D offset
     0058 A9F1                 adca    [,s++]          ; indirect auto incr.
     005A A9D1                 adca    [,u++]          ; indirect auto incr.
     005C A991                 adca    [,x++]          ; indirect auto incr.
     005E A9B1                 adca    [,y++]          ; indirect auto incr.
     0060 A9F3                 adca    [,--s]          ; indirect auto incr.
     0062 A9D3                 adca    [,--u]          ; indirect auto incr.
     0064 A993                 adca    [,--x]          ; indirect auto incr.
     0066 A9B3                 adca    [,--y]          ; indirect auto incr.
     0068 A99C95               adca    [label5,pcr]
     006B A99CE8               adca    [label3,pcr]
     006E A99D001A             adca    [label4,pcr]
     0072 A99D0484             adca    [label2,pcr]
     0076 A9E4                 adca    ,s
     0078 A9C4                 adca    ,u
     007A            
     007A            
     007A A99C83               adca    [label5,pcr]
     007D A99C80               adca    [label5,pcr]
     0080 A99DFF7C             adca    [label5,pcr]
     0084 A99DFF78             adca    [label5,pcr]
     0088            
     0088            
     0088 A984                 adca    ,x
     008A A9A4                 adca    ,y
     008C C913         label4: adcb    #$13            ; immediate data
....
     04FA              label2:
     04FA 2FFE                 ble     label2
What puzzles me is the code at 006E. The lines 007A..0084 proof that if the difference is less than 128 bytes, the short method is used. But the code at 006E uses the long method although the difference is smaller than 128 bytes. The books I have are not clear enough about it and my own assumption is that the code had should been A9 9C 1A. Another thing: I saw various combinations of code using XX 9C 80..FE but never in the range 00..7F.

Can anybody confirm that or I missing something? TIA!
 
Labels 3 and 5 are defined before they are used, so the assembler 'knows' that it can lay down the short form.

However, labels 2 and 4 are defined after they are used. On pass 1, the assembler has to assume the worst, so it lays down the long form, even though the human is aware (and the assembler will be aware on pass 2) that the assembler could have used the short form of the instruction for label 4. By that time, it is too late!

If the assembler contained further passes, it could optimise the code it generates.

Dave
 
That old assembler is indeed a two-pass assembler. OK, that explains everything, thank you very much!
 
I had to think things over, quite hard, because this is a new situation for me. I do have a multi-pass assembler but it was meant to handle variables that contained variables declared after the original ones. But the mechanism can be used IMHO for this idea as well.
The idea is to use long addressing mode for all instructions that point to a label/variable defined after this instruction. If a label/variable is encountered within the short range, the instruction code is altered. That means one byte less is needed but also that the label/variable will move down (at least) one byte. That will be detected when the assembler encounters this label/variable and discovers that the old value is higher than the new one: the "another pass needed" flag is set. This will trigger at the end of the file to run a new pass. This will go on until at the end of a pass this flag has not been set. Then a final pass is run to put the dot on the i.

Any comments on this idea?
 
You could do that. But it could iterate for quite a while. Maybe it would be better to just provide a way for the programmer to specify that short addressing should be used, and give an error during pass 2 if it won't work.
 
Which is the option that Intel took with the 8086 assembler. Something like:

JZ SHORT FRED

or something like: JZ.S FRED (as an alternative).

The trouble is that you then put the hassle on the human... And (as I know) you can come to hate the original programmer for doing this very quickly if you subsequently modify the source code...

Dave
 
I spent a little time with 6809 assemblers while playing with Color Forth on the CoCo: https://forum.vcfed.org/index.php?threads/color-forth-available-for-coco-with-decb.46819/

The results were quite interesting: the original code was assembled with EDTASM that is probably a single pass assembler. The modern cross assemblers asm6809 and lwasm look to do more passes. I ended up using lwasm because it was more aggressive and generated the smallest binary. I was able to shrink the binary enough to add a bunch of features and still be smaller than the EDTASM generated binary.
 
Old versions of MASM for x86 assumed the long form in the first pass, and would generate the short form (plus a NOP as padding) when possible. If the programmer specified the short form, it would always be generated (or an out-of-range error).

Introducing additional passes to optimize can lead to infinite oscillations.
 
Yes, it's all about whether it knows the value yet or not. If it doesn't know the value, it has to make room for the largest possible offset.

Motorola had a syntax for you to override the size to short, use "<label,pcr" and the less-than character forces a short offset. This feature can be important when disassembling code, so that you can generate identical results on re-assembly. Also, feel free to try my assembler (see sig) and see if it can work for you.
 
To solve the 'infinite optimisation looping' you specify a 'depth' parameter (well, on the various assemblers I have used you do). This limits the iterations to something sensible - but still defined by the human.

Dave
 
In an optimizing pass, the assembler may change a long jump into a short jump to save a byte.

This causes addresses further down to shift and may enable/disable further optimizations (aligned tables may move around, additional padding may happen, short jumps may no longer reach, long jumps could now reach even if they were short, etc). During a single pass, changes will only ripple downwards through the code, but they may open up opportunities for further optimization in earlier code areas as well. To catch those, another optimization pass is needed.

Trivially, optimization is finished if an optimizing pass no longer causes a change - a steady state. But if one optimization pass ends up reversing a decision made in a previous pass (possible since some optimizations can cause addresses to shift in both directions), such a steady state may never be reached. The code (size) will start to oscillate instead and optimization will never finish.
 
A new question:

ADCA [$1234] will end up as A9 9F 12 34
ADCA $1234 will end up as B9 12 34

I'm also creating a disassembler. The datasheet of the 6809 doesn't mention that an instruction, where bit 4 of the postbyte is zero ,is not allowed. That would mean that:

A9 8F 12 34 can be disassembled into ADCA $1234

The main question: is this a legal instruction? (If it make sense is another question :) )

Thank you in advance!
 
>>> The main question: is this a legal instruction?

Well, that is a very wide question indeed...

On the 8086 (for a couple of instructions) it is possible to achieve the same effect by coding up the instruction stream in two different ways. These are 'legal' bit patterns (according to the Intel documentation). This has been seen 'in the wild for real' when a development platform was changed from a PDP-11 (running an Intel cross-assembler from BSO) to an IBM-PC DOS environment (running an Intel assembler from 2500AD). For the same source code, both assemblers produced slightly different output bytes - one assembler using one of the encoding options and the other assembler using the other. This made it more difficult to run a binary comparison on the assembled output file...

In your case it looks slightly more obscure.

The indexed addressing mode table I have indicates that 8F is blank for a 6809 CPU:

1711533221362.png
9F is OK as [nnnn] but the corresponding 8F is blank.

Further up the table is an entry for 8F - but it is related to the 6309 CPU:

1711533349135.png

So you now have a couple of choices:

1. On a 6809, indexed addressing mode byte 8F does work - it is just that Motorola have not bothered to describe it because there is a shorter instruction stream of achieving the same effect; and the shorter instruction will be more compact and faster and (as a result) Motorola want you to use this form.

2. On a 6809, indexed addressing mode byte 8F is either not implemented or does not work as intended...

3. On a 6809, indexed addressing mode byte 8F is reserved for the 6309.

I would take the path of least resistance (if coding up a disassembler) and assume that this pattern does not work and is illegal.

Dave
 
First: thank you for answering all of my questions!

The indexed addressing mode table I have indicates that 8F is blank for a 6809 CPU:
That's the same in the datasheet I have. But for the post bytes [,R+] and [,-R] it states these are "not allowed". That blank confused me and that's why I asked.

I think, for the moment, the only thing I can do is to dig up an emulator somewhere and to code a little program that can test this. And THAT is going to be a big job because the last ML program I wrote for the 6809 was 38 years ago :). And even then, I wrote it in 6502 ML and translated it to 6809. The only complaint my teacher had was that I forgot most of the time that the 6809 also had 16-bit registers.

For the moment I don't worry about it. If it is not a valid set of codes, there won't exist any assembler creating it. And in that case I won't stumble over it. :)
 
>>> First: thank you for answering all of my questions!

Absolutely no problem...

Blanks could mean anything (unfortunately).

I always assume that documentation contains errors. When I write an emulator, I try to find multiple copies of documentation for the CPU. However, you have to beware that one person may have 'copied' someone else's documentation and included the same errors plus their own errors in re-writing it!

I was involved in getting the 6809 SuperPET addition to VICE up and running (along with others of course). I started off with a 6809 emulator that I found on the internet. It was awful and (quite literally) full of bugs. I considered fixing it but (in the end) I junked it and found something else. The replacement was pretty good.

I did find a 6809 validation suite - this failed one or two instructions, but I was able to fix these.

I would probably suggest using VICE, selecting the SuperPET and downloading your own firmware?

Just a thought for you to consider.

I agree with you. Hopefully, as a disassembler, if you come across this instruction coding you might have crashed into data...

Dave
 
Having been there over 20 years ago, I can tell you that basically no "undocumented" 6809 instructions are useful. As mentioned above, there were some cases of redundant addressing modes that just took more clock cycles, but there are only two worthy of note. First is the TEST (aka HCF) instruction, $14 and $15, particularly annoying to me because I had (and actually still have) a 6809 ICE that used the same CPU for both target and host, so it killed everything when you hit one of those. The second was the alternate pre-byte versions of SWI. I recall that there was one that went though the FIRQ vector but pushed all registers, and another that (apparently) went through the RESET vector, but I'll bet it also pushed all registers.
 
Back
Top