vpoke() for cc65

Chat about anything CX16 related that doesn't fit elsewhere
Post Reply
rje
Posts: 1263
Joined: Mon Apr 27, 2020 10:00 pm
Location: Dallas Area

vpoke() for cc65

Post by rje »


Painful, I know.  If I shoved this into asm() statements would it be better?

#include <peekpoke.h>
#include "vpoke.h"

void vpoke(uint16_t address, uint8_t value)
{
    POKE( 0x9f20, address & 0xff ); // LSB
    address >>= 8;
    POKE( 0x9f21, address & 0x0f ); // 4 low bits of the MSB
    POKE( 0x9f22, 1 ); // VERA "bank 1"
    POKE( 0x9f23, value );
}

Elektron72
Posts: 137
Joined: Tue Jun 30, 2020 3:47 pm

vpoke() for cc65

Post by Elektron72 »



31 minutes ago, rje said:




If I shoved this into asm() statements would it be better?



Unless cc65 is smart enough to optimize out the bit shifts and AND operations (which is unlikely), an assembly version of this function would be much faster, as the MSB and LSB of the address could easily be accessed separately.


34 minutes ago, rje said:




POKE( 0x9f21, address & 0x0f ); // 4 low bits of the MSB



I don't understand why the code is masking off the upper bits of the MSB, as VERA addresses are 17 bits (8 bit LSB + 8 bit MSB + 1 bit bank selection).

rje
Posts: 1263
Joined: Mon Apr 27, 2020 10:00 pm
Location: Dallas Area

vpoke() for cc65

Post by rje »



2 minutes ago, Elektron72 said:




I don't understand why the code is masking off the upper bits of the MSB, as VERA addresses are 17 bits (8 bit LSB + 8 bit MSB + 1 bit bank selection).



VERA addresses are indeed 17 bits; however, we have to set those 17 bits across three bytes: 0x9f20 gets the LSB, 0x9f21 gets the "middle", and 0x9f22 gets the bank.  That points us at our destination address in VERA.  THEN we can write the byte.

Elektron72
Posts: 137
Joined: Tue Jun 30, 2020 3:47 pm

vpoke() for cc65

Post by Elektron72 »



1 minute ago, rje said:




VERA addresses are indeed 17 bits; however, we have to set those 17 bits across three bytes: 0x9f20 gets the LSB, 0x9f21 gets the "middle", and 0x9f22 gets the bank.  That points us at our destination address in VERA.  THEN we can write the byte.



$9F21 accepts 8 address bits; ANDing the MSB with 0x0f would discard bits 12-15 of the address.

rje
Posts: 1263
Joined: Mon Apr 27, 2020 10:00 pm
Location: Dallas Area

vpoke() for cc65

Post by rje »



Just now, Elektron72 said:




$9F21 accepts 8 address bits; ANDing the MSB with 0x0f would discard bits 12-15 of the address.



LOL. you're right.  

 

rje
Posts: 1263
Joined: Mon Apr 27, 2020 10:00 pm
Location: Dallas Area

vpoke() for cc65

Post by rje »


Turns out that cc65 has vpoke already... and a lot more than that.

https://github.com/cc65/cc65/blob/master/include/cx16.h

ZeroByte
Posts: 714
Joined: Wed Feb 10, 2021 2:40 pm

vpoke() for cc65

Post by ZeroByte »


I don’t know what poke and peek compile to in assembly, but here’s a way to make it be simple under the hood:

(*(uint8_t*)0x9F40) = 0x08

= POKE $9F40,8

If you use an explicit address a lot, then you can “name” it with a #define

#define YMreg (*(uint8_t*)0x9F40)

YMreg = 8;

 

Of course, common system things like VERA, VIA, etc already have these in cx16.h but if there’s a particular address you want to access that’s not already defined in a macro for you, that’s how to do it.

 

rje
Posts: 1263
Joined: Mon Apr 27, 2020 10:00 pm
Location: Dallas Area

vpoke() for cc65

Post by rje »


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.

TomXP411
Posts: 1760
Joined: Tue May 19, 2020 8:49 pm

vpoke() for cc65

Post by TomXP411 »



1 hour ago, rje said:




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.



This obviously could use a helper function to convert the increment, bank number, and channel number to the correct data values to go in the structure. Using this structure is only mildly better than simply brute forcing it, as you did with your first function (although it needs a bank parameter, which I'm sure you already know.)

Remember, there are 2 data channels in VERA, and using both of them together is a good way to solve certain problems. So that bit needs to be added to any routine that builds the bit masked control data for writes to or reads from VERA.

Post Reply