# A Component Object Model ABI for the 65816
I don't have much time,
so I'm not able to go into the nitty-gritty details of how everything here works.
I may come back to this blog post and refine the article as more time permits.
The problem with the 65816 is that you cannot call a subroutine whose address has been computed.
I mean, you can, but it is horrifically inefficient.
The most obvious way to invoke an indirect long-address vector is:
_AL ; use 16-bit accumulator
_XS ; use 8-bit index registers
LDX vecptr+2 ; push bank address byte
PHX
LDA vecptr ; push bank offset (assume it's pre-decremented)
PHA
RTL
This totals 27 cycles, and it destroys the contents of all registers in the process.
This is highly undesirable,
and this assumes vecptr resides in direct page.
Add two more cycles if it's accessible via absolute addressing in the current bank,
and four more cycles if it resides in another bank all-together.
Through clever pointer representation, though,
we can at least fix the destroyed registers problem.
We can't do too much about the time cost of calling a method, unfortunately.
Let each application define its own concept of a pointer in direct page called
THIS. Its purpose is to point to the current object whose method you are
invoking. THIS resides in direct page and is a long pointer.
Referencing instance data for the object/component happens easily enough: load
Y with the offset, and use [THIS],Y addressing.
To invoke a method on the component, load X with the method ID (0, 2, ..., 2N-2
for an interface with N methods), then JSL to THIS-1. Let's call this address
CALLTHIS. At CALLTHIS, there **must** be an opcode byte for JML.
Thus, the instance data for the component itself must bounce the call to the
appropriate interface implementation.
So, we have a data structure layout like so:
Direct Page
CALLTHIS | (JML) |
+--------+ Component
THIS | THIS L | .
+--------+ | +--------+
| THIS M | |-----------> | (JML) | +0
+--------+ | +--------+ Class Implementation
| THIS H | " | IMPL L | +1 .
+--------+ | +-------------+
| IMPL M | +2 |-----------> | JMP (*+3,X) |
+--------+ | +-------------+
| IMPL H | +3 " | my_queryIf |---->
+--------+ +-------------+
| .... | +4 | my_addRef |---->
+-------------+
| my_release |---->
NOTE: Both the class implementation and the application need to agree on which
direct page address corresponds to THIS. Otherwise, the class implementation
cannot find the instance data for the interface invoked!
This method is slow, as the table illustrates. However, compared to every
other approach for polymorphism on the 65816 platform, this is one of the
fastest methods of dispatch that is compatible with the entire 16MB address
space and set of native-mode capabilities.
| Instruction | Cycles |
|:-------------|:------:|
| LDX #$nnnn | 3 |
| JSL CALLTHIS | 8 |
| JML instance | 4 |
| JML vtable | 4 |
| JMP (x,X) | 6 |
| RTL | 6 |
|--------------|--------|
| TOTAL | 31 |
For functional programming, things are actually a *little* bit faster. This is
because the component indirection can be skipped entirely. Since "rich
pointers" consist of both a record pointer _and_ an interface pointer (which
the compiler always keeps in sync), we skip the component all-together.
| (JML) |
+--------+
| VTAB L | .
+--------+ | +-------------+
| VTAB M | |--------------------------> | JMP (*+3,X) |
+--------+ | +-------------+
| VTAB H | " | func_1 |
+--------+ +-------------+
| DATA L | . | func_2 |
+--------+ | +------------+ +-------------+
| DATA M | |-----> | ...data... | | func_3 |
+--------+ | | | +-------------+
| DATA H | " +------------+ | ... |
+--------+
| $00 |
With this mechanism, you need only store the v-table pointer in an absolute
location for easy JSL-ing. The data pointer can remain anywhere else
convenient, as long as the function called and the caller agree on where to
find the instance data pointer. In the example above, I co-locate the
pointers in direct page, but this is not strictly necessary.
OK, I gotta go. Run with this if y'all want; I only ask that you kindly give me credit if you decide to use it.