Yeah, that's basically how cc65 does it.
Just so I can practice thinking about it, here's how cc65 does it. The header file cx16.h has a nice pile of VERA stuff defined. The main bit starts here:
#define VERA (*(volatile struct __vera *)0x9F20)
So it impresses the struct __vera onto the VERA registers. That structure is really nice, basically laying out the registers so we can simply assign to them directly. It starts like this (and goes on and on):
/* A structure with the Video Enhanced Retro Adapter's external registers */
struct __vera {
unsigned short address; /* Address for data ports */
unsigned char address_hi;
unsigned char data0; /* Data port 0 */
unsigned char data1; /* Data port 1 */
unsigned char control; /* Control register */
...
So for example if I wanted to store a byte on VERA at $1FC10, I'd do this (thank you
@Greg King!)
VERA.control = 0; // point to Data port 0.
VERA.address = 0xfc10; // the address inside VERA.
VERA.address_hi = 1; // + the "bank" address inside VERA ($10000)
VERA.data0 = the_byte; // my data on Data port 0.
NOTE: the .address_hi field is also where you would set the auto increment. So it's not 100% clean... but it's very helpful.