EXEC dispatch table for 6809 assembly

I am writing this so one of the 6809 experts who reads this can chime in and tell me a better way…

Often I post things so they can get in the search engines in case anyone else looks for that topic later. This is one of those.

Using DEF USR is a great way to put up to ten “easy to execute” routines in an assembly language program. Each of those routines can also do different things based on the numeric (or string) parameter passed in to the USR() call.

If you aren’t trying to be that fancy, but do want multiple functions for whatever reason, what methods are there? Please leave a comment with the best ways to call multiple functions using EXEC from Color BASIC.

Dispatch table

One method that comes to mind is using a dispatch table at the start of the machine language program. If the code is built to compile at &H3F00, then doing an EXEC &H3F00 will run that program. If there are more functions, you have to figure out where they are located and provide those address to the user. This is fine, until you make a change to the code and then those locations shift.

Instead, the start of the program could begin with a series of “branch always” instructions. For example:

            org     $7f00

start1 bra install
start2 bra uninstall

The branch always instruction is one byte, and it is followed by a second byte which is how many bytes away the function is. This makes each entry take two bytes. Thus, install is at &H7F00 and uninstall is at &H7F02. A whole series of functions could be done this way, and the user just has to remember which is which — &H7F00, &H7F02, &H7F04, etc. Having every two bytes be an entry makes it easy to remember.

; lwasm dispatch.asm -fbasic -odispatch.bas --map
; a09 -fbasic -odispatch.bas dispatch.asm

ORGADDR equ $3f00 ; Where program loads in memory

org ORGADDR

;------------------------------------------------------------------------------
; Absolute addresses of ROM calls
;------------------------------------------------------------------------------
CHROUT equ $A002

;------------------------------------------------------------------------------
; This code can be called by EXEC/EXEC xxxx.
;------------------------------------------------------------------------------
; Dispatch table at the start of the program.
start1 bra install
start2 bra uninstall

install leax <msginst,pcr ; X points to message
bra print ; print will do the RTS
;rts

uninstall leax <msguninst,pcr ; X points to message
;bra print ; print will do the RTS
;rts

;------------------------------------------------------------------------------
; PRINT subroutine. Prints the 0-terminated string pointed to by X plus CR
;------------------------------------------------------------------------------
print lda ,x+
beq printdone
jsr [CHROUT]
bra print
printdone lda #13
jmp [CHROUT] ; JMP CHROUT will do an rts.
;rts

;------------------------------------------------------------------------------
; Data storage for the string messages
;------------------------------------------------------------------------------
msginst fcc "INSTALLED"
fcb 0

msguninst fcc "UNINSTALLED"
fcb 0

end

One potential issue is that branch can only jump so far. If large functions are being called, you might find they cannot be reached from this dispatch table. One option would be to switch to “long branch”, but then you add more bytes and your dispatch table might be every three bytes – &H7F00, &H7F03, &H7F06, &H7F09, &H7F0C, etc.

That is a fine solution though every 2 may “look” nicer than every 3.

As a workaround, the dispatch table could remain short branches, but they go to a longer one just below it:

            org     $7f00

start1 bra install
start2 bra uninstall

; If a short branch cannot reach, it can call a second long branch:
uninstall lbra realuninstall

Above, perhaps “install” is within reach of the “bra”, but “uninstall” is too far away. Simply make the “bra uninstall” branch to a spot with a long branch. A few more bytes, a few more clock cycles, but now the dispatch table can remain “every 2 bytes”.

But there has to be a better way…

Leave your suggestions in the comments.

Until next time…

Bonus

Here is a BASIC loader for that example. RUN it, then EXEC &H7F00 or &H7F02 and be amazed. (Loader generated using Sean Conner’s a09 assembler.)

10 DATA32,2,32,5,48,140,21,32,3,48,140,26,166,128,39,6,173,159,160,2,32,246,134,13,110,159,160,2,73,78,83,84,65,76,76,69,68,0,85,78,73,78,83,84,65,76,76,69,68,0
20 CLEAR200,16127:FORA=16128TO16177:READB:POKEA,B:NEXT:

6 thoughts on “EXEC dispatch table for 6809 assembly

  1. William Astle

    A list of LBRAs or JMPs is about the best way if you’re targetting launching via EXEC. You can use BRA if you can guarantee you won’t ever need to exceed an 8 bit displacement. The extra byte for LBRA (or JMP) isn’t really much of a concern in most programs and if you’re launching with EXEC, the cycle savings of using BRA vanish in the noise. You can use JMP if you don’t need position independence, or if you’re just proxy vectoring to something like a ROM routine. So I’d stick with the every three bytes entry points.

    Reply
    1. Allen Huffman Post author

      I was mostly thinking how it’s easier to add by 2 than by 3, especially when it comes to hex addresses.

      Next I wondered why Microsoft had nice dispatch things like [A002] then decided other “documented ROM calls” would just be the direct location. I can only imagine how much nicer the ROM would have been if they could have seen into the future.

      Reply
  2. Sean Patrick Conner

    You want to use EXEC to avoid being “fancy?” The reason why my assembler outputs not only the data statments and the code to poke them into memory, but also the DEFUSRn statements was to avoid having to do all that by hand. Demand more of your assembler!

    Reply
    1. Allen Huffman Post author

      I am thinking more like all the assembly stuff I used “back in the day” like remote terminal drivers and such. LOADM”REMOTERM” and such. One redirected console output to the bitbanger, but you had to use POKEs to set parameters then do an EXEC to get the INPUT back in and stuff. I had a notepad with all the addresses next to my computer for reference when I was doing that. And when the next version came out, I had to edit my BASIC program using it with different values.

      Reply
      1. Sean Patrick Conner

        One radical idea is to extend BASIC with new keywords. I did a “proof-of-concept” that uses the “user command interpreter table” where all that’s needed is to exec one address, and your program gets new tokens to play with. Of course, having the Unravelled Series to call undocumented BASIC functions didn’t hurt any …

        Reply
        1. Allen Huffman Post author

          On my list of things I’d like to learn are… adding new commands, and doing things like disable break and add ON ERROR GOTO type stuff. I saw many programs that did this, and always wondered how they worked.

          Reply

Leave a Reply to Allen HuffmanCancel reply

This site uses Akismet to reduce spam. Learn how your comment data is processed.