wdc816cc bank number from long address?

This is an archive of a topic from NESdev BBS, taken in mid-October 2019 before a server upgrade.
View original topic
wdc816cc bank number from long address?
by on (#132979)
Hi,

does anybody know if it's possible to get the bank number from a statically allocated address in wdc 816 c compiler? When I use the shift operator to get the bank number from the 8 high bits of the 24-bit address, then the compiler generate some 24-bit arithmetics code, even though the result of the operation is a constant which should be known at compile time.

I know assemblers typically have a macro to get the bank number from symbols, but I would really like to do it in c.

Cheers
Dalton
Re: wdc816cc bank number from long address?
by on (#132992)
Did you check the documentation that comes with WDC816CC? It should be in there. If not, contact WDC -- they should be able to tell you how to do this. I think you have to shift out the 32-bit calculated address from either the CODE, DATA, UDATA, or KDATA section names (defined per #pragma). These are mentioned in the "Pragma" section of the doc, with a brief code example in "Heap Functions". It's not completely clear though, I'll tell you that, and it depends on what memory model you're using/have configured.

Their assembler documentation (note I said ASSEMBLER DOCUMENTATION, meaning stuff you'd have to put in in-line assembly within C) indicates that you'd use >> to get to the bank, however in their own code examples they don't even use this, they use ^, ex. LDA #^_BEG_DATA, so I don't know what to tell you. There's also mentioned of something called DBREG, which looks like it's a pseudo-op that tells the assembler what bank you're currently in (think of it as an assemble-time way of tracking B) so that instead of generating 24-bit (long) addresses it generates 16-bit addresses. Search for "This directive is used to indicate to the assembler the run-time value of the Data Bank register".

Honestly you're better off just Emailing WDC and asking for some examples/clarification.

If you're trying to do C programming on the SNES, I'd recommend just avoiding it and using assembly instead. That's my own opinion though and you can totally disregard it if you want.

References:

Re: wdc816cc bank number from long address?
by on (#132999)
Thank you for your reply!

I've had very little luck contacting WDC before :( Even when I asked about purchasing software they did not reply.

To be more specific about what I'm trying to do, I have this function which copies data to vram:

Code:
#define L(x) ((uint8_t)((x)&255))
#define H(x) ((uint8_t)((x)>>8))

typedef signed char    int8_t;
typedef unsigned char  uint8_t;
typedef signed short   int16_t;
typedef unsigned short uint16_t;


void vram_write(uint16_t dest, uint8_t src_bank, uint16_t src_addr, uint16_t size)
{
   VMAIN = 0x80;         // increment address after word has been written
   VMADD = dest;         // write to this address in vram

   DMAP0 = 0x01;         // set DMA control register (1 word inc)

   BBAD0 = L((uint16_t)&VMDATA);   // set destination ($21xx xx -> 0x18)

   A1B0 = src_bank;
   A1T0 = src_addr;

   DAS0 = size;

   MDMAEN = 0x01;
}


I'm looking for a way to calculate the src_bank argument, or even better to pass just a long pointer instead of of src_bank and src_addr.

Perhaps a macro could be written that uses inline asm to split the long pointer into bank/addr and write registers A1B0 and A1T0?
Re: wdc816cc bank number from long address?
by on (#133001)
I don't know what to tell you. The Compiler Manual seems pretty clear about what each of the memory models represent, and the section called "Memory Management" is what mentions the sections referred to as CODE, DATA, UDATA, and KDATA, as well as how the keywords near and far play a role in all of this. I strongly recommend you read that section, as well as all the example code they provide. Anything with a calculated 24-bit address (far) should be easy to obtain the bank -- you just >> 16 to shift off the lower 16 bits and what you're left with is the bank. Also be sure to read the section titled "Large Programs".

The reason I mentioned #pragma is because that appears to be how you can explicitly set a bank for certain types of sections (CODE, DATA, and UDATA). There is a short example in the "Heap Functions" area that shows how they're using "magic variables" called _END_UDATA and so on to obtain addresses of things -- it's these "magic variables" which are what you're effectively looking for. It doesn't matter if you're using inline assembly or C, both methodologies are going to require that you refer to something that the assembler and/or compiler tracks internally -- that's just how it works, as I'm sure you know. But if you wanted to try and use inline assembly, then you should look at the Assembler/Linker Manual, because that goes over all the internal variables the assembler has.

You're probably going to have to spend a very long while playing with this and examining the full output of a listing generation, as well as a full assembly listing, to ensure everything is "correct", especially considering the SNES's memory map -- you need to see exactly what the compiler and/or assembler are generating binary-wise to know if things are correct/what you expect.

WDC's compiler really isn't directed at SNES stuff in general, and the example programs I've seen online of people trying to use it for SNES developer show nothing useful, e.g. very small programs that fit exclusively within bank $00 thus their entire program has that hard-coded into it. Ten bucks if they wrote something >32KBytes in size they'd be asking the same thing you are.

I only know of 3 C compilers for the 65816: WDC's, ORCA/C (solely for the Apple IIGS), and Toshi Morita's LCC (which just generates assembly code intended for use with ORCA/M which is also solely for the Apple IIGS). Your choices are extremely limited. Considering WDC's product (last I read) cost US$40, then I'd say you should start making some telephone calls to WDC instead of Email.
Re: wdc816cc bank number from long address?
by on (#133004)
I did some testing with wdc816cc a while back (hirom, using more than one bank, lorom was a pain in the side), you can check it out here https://github.com/ARM9/snes-c-template
If you want to use dma from C you can use this macro (lifted from snes-sdk I believe):
Code:
#define DMA_TYPE(chn,x) (*(unsigned int*)(0x4300 | ((chn) << 4))) = (x)
#define DMA_ADDR(chn,x) (*(void**)(0x4302 | ((chn) << 4))) = (x)
#define DMA_SIZE(chn,x) (*(unsigned int*)(0x4305 | ((chn) << 4))) = (x)

//chn: constant, channel number 0-7 (preferably use only one channel for all dma transfers to free up hdma channels)
#define dmaTransfer(type, addr, size) DMA_TYPE((0), (type)); \
      DMA_ADDR((0), (addr)); \
      DMA_SIZE((0), (size)); \
      REG_MDMAEN = BIT(0);

It optimizes fairly well so just throw it in functions for different dma types (vram, cgram etc) to avoid too many macro expansions.
For some reason it didn't want to optimize out the subtractions when I calculated the size of a binary insert in C, might want to look into that.
Re: wdc816cc bank number from long address?
by on (#133006)
Well, my problem is not so much finding out where stuff is located (not yet, at least). But the thing is that I wish to avoid the over-complicated construct generated by the compiler in this situation.

Code:
   far void *my_pointer;
   unsigned char my_bank;

   my_pointer = (far void *)0x123456;
   my_bank = (unsigned long)my_pointer >> 16;


It's fairly obious from the code that 0x12 should be stored in my_bank, but the compiler generates a call to some library function called llsr. Not exactly optimal :(

Code:
   lda   #$3456
   sta   <L3+my_pointer_1
   lda   #$12
   sta   <L3+my_pointer_1+2
   pha
   pei   <L3+my_pointer_1
   dea
   dea
   xref   __~llsr
   jsr   __~llsr
   sta   <R0
   stx   <R0+2
   sep   #$20
   longa   off
   lda   <R0
   sta   <L3+my_bank_1