Page 1 of 1

vpoke() for cc65

Posted: Wed Jul 28, 2021 12:50 am
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 );
}


vpoke() for cc65

Posted: Wed Jul 28, 2021 1:27 am
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).


vpoke() for cc65

Posted: Wed Jul 28, 2021 1:30 am
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.


vpoke() for cc65

Posted: Wed Jul 28, 2021 1:41 am
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.


vpoke() for cc65

Posted: Wed Jul 28, 2021 1:43 am
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.  

 


vpoke() for cc65

Posted: Wed Jul 28, 2021 1:43 am
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


vpoke() for cc65

Posted: Wed Jul 28, 2021 12:56 pm
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.

 


vpoke() for cc65

Posted: Wed Jul 28, 2021 3:11 pm
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.


vpoke() for cc65

Posted: Wed Jul 28, 2021 4:20 pm
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.