Exposing Variable from CA65 to CC65
Moderator: Moderators
Exposing Variable from CA65 to CC65
I have a variable that I want to be able to access in both my C code, and my Assembly code.
Can someone provide an example of how to do this?
Everything i have tried either ends up with unresolved external symbols, redefinition of the variable, or 2 separate variables which give different values depending on if they are accessed in C or assembly.
Can someone provide an example of how to do this?
Everything i have tried either ends up with unresolved external symbols, redefinition of the variable, or 2 separate variables which give different values depending on if they are accessed in C or assembly.
- rainwarrior
- Posts: 8006
- Joined: Sun Jan 22, 2012 12:03 pm
- Location: Canada
- Contact:
Re: Exposing Variable from CA65 to CC65
Anything in C has a _ on the front in assembly. Anything in assembly with an _ on the front can be exported from assembly with .export and "imported" with extern in C.
When I want to use something in both, I usually make an alias with a _ to export to C:
And going the other way...
Also, if you want to be able to take advantage of zero page optimizations, you can allocate a variable in assembly and use a pragma to tell the C code it's on the zero page.
If you'd like some examples, my NESert Golfing project goes back and forth between C and assembly a lot. See dgolf.c and dgolf.s.
This document might also help if you're trying to write functions in assembly that you can call from C: cc65-intern
When I want to use something in both, I usually make an alias with a _ to export to C:
Code: Select all
; assembly
.segment "BSS" ; (it can be any segment)
variable: .res 1
_variable = variable ; alias with a _
.export _variable
// C
extern char variable;
Code: Select all
// C
char variable;
; assembly
.import _variable
variable = _variable ; alias without the _
Code: Select all
; assembly
.segment "ZEROPAGE"
_i: .res 2
.exportzp _i
// C
extern int i;
#pragma zpsym("i");
This document might also help if you're trying to write functions in assembly that you can call from C: cc65-intern
Re: Exposing Variable from CA65 to CC65
2 ways.
In the C declare the variable extern and in the asm declare it with _ prefix and write a .export _blah at the top of the file declaring it
or
In the C declare as normal. In the asm write a .import _blah in the file that uses it (with _ prefix in asm files)
In the C declare the variable extern and in the asm declare it with _ prefix and write a .export _blah at the top of the file declaring it
or
In the C declare as normal. In the asm write a .import _blah in the file that uses it (with _ prefix in asm files)
nesdoug.com -- blog/tutorial on programming for the NES
Re: Exposing Variable from CA65 to CC65
I must be missing something (I am trying the "exporting from ASM" version that rainmaker demonstrated).
It compiles, but when I look at the resulting assembly, they are pointing to 2 different spots in memory.
Code incrementing the assembly version:
Code checking the value in C:
It compiles, but when I look at the resulting assembly, they are pointing to 2 different spots in memory.
Code: Select all
; assembly
.segment "BSS" ; (it can be any segment)
CREDITS_QUEUED: .res 1
_CREDITS_QUEUED = CREDITS_QUEUED ; alias with a _
.export _CREDITS_QUEUED
// c header
#pragma bss-name(push, "BSS")
extern char CREDITS_QUEUED;
Code: Select all
; inc <CREDITS_QUEUED
INC $1B
Code checking the value in C:
Code: Select all
;if (CREDITS_QUEUED > 0)
L1970:
LDA $041B
BEQ L0DDC
Last edited by Goose2k on Tue Dec 01, 2020 7:00 pm, edited 1 time in total.
- rainwarrior
- Posts: 8006
- Joined: Sun Jan 22, 2012 12:03 pm
- Location: Canada
- Contact:
Re: Exposing Variable from CA65 to CC65
I can't tell you what's wrong given your description. What you've written looks okay, so you'd have to share more of the program to try and diagnose it.
This line is meaningless in this context. The bss-name determines where new variables declared in C are allocated. Ones that are extern (imported) are already assigned their own segment elsewhere and not affected by this pragma.
Code: Select all
#pragma bss-name(push, "BSS")
Re: Exposing Variable from CA65 to CC65
Oh I see the issue now that I wrote it in the post above. I had:
inc <CREDITS_QUEUED
But it should have been changed to
inc CREDITS_QUEUED
I think because I moved it out of ZP? (trying to match your example)
inc <CREDITS_QUEUED
But it should have been changed to
inc CREDITS_QUEUED
I think because I moved it out of ZP? (trying to match your example)
- rainwarrior
- Posts: 8006
- Joined: Sun Jan 22, 2012 12:03 pm
- Location: Canada
- Contact:
Re: Exposing Variable from CA65 to CC65
Your assembly code is incorrect. You have force-discarded the high byte of its address with <. I would generally recommend avoid using this operator this way in ca65.Goose2k wrote: ↑Tue Dec 01, 2020 6:54 pmCode incrementing the assembly version:Code: Select all
; inc <CREDITS_QUEUED INC $1B
Code checking the value in C:Code: Select all
;if (CREDITS_QUEUED > 0) L1970: LDA $041B BEQ L0DDC
So you're probably used to seeing this in NESASM:
Code: Select all
inc <CREDITS_QUEUED
Code: Select all
inc z:CREDITS_QUEUED
Most of the time z: is unnecessary in ca65, because it will automatically use ZP instructions as long as the variable is declared above its usage.
Last edited by rainwarrior on Tue Dec 01, 2020 7:09 pm, edited 1 time in total.
Re: Exposing Variable from CA65 to CC65
Great explanation thanks!
Funny enough, it's used like that throughout shiru's neslib, which is where I picked it up from.
Funny enough, it's used like that throughout shiru's neslib, which is where I picked it up from.
- rainwarrior
- Posts: 8006
- Joined: Sun Jan 22, 2012 12:03 pm
- Location: Canada
- Contact:
Re: Exposing Variable from CA65 to CC65
Replied while I was editing, but I just wanted to point out that in ca65 you probably don't want to use z: under normal circumstances. ca65 automatically selects ZP instructions, so it's usually unnecessary. z: is only needed if you want the compiler to make sure the variable was on the ZP.
In cc65:
NESASM doesn't auto-select, so the < notation it uses becomes mandatory any time you want ZP access. A lot of people coming from NESASM to ca65 have a habit of using < everywhere they use ZP.
In cc65:
Code: Select all
lda <var ; "I know for sure this is on the ZP. Don't check."
lda z:var ; "I think this is on the ZP. Please make sure."
lda var ; "I'll let the assembler figure it out."
Re: Exposing Variable from CA65 to CC65
Question: Does ca65 properly use zero-page addressing if the "z:" operator is used on an instruction which argument is memory outside the zero-page area?
- rainwarrior
- Posts: 8006
- Joined: Sun Jan 22, 2012 12:03 pm
- Location: Canada
- Contact:
Re: Exposing Variable from CA65 to CC65
It will always generate a zero-page instruction if you use z: but if the argument isn't actually on the zero-page, it will give an error and it will fail to build.
If you want to force a zero-page instruction on something that is not on the zero-page, you can use < to intentionally discard the high bytes of the address.
Otherwise without a prefix it will correctly pick the zero-page instruction in most cases. As long as enough information is given on a line before that instruction, it will know it belongs there. (Either the label's segment is marked with : zeropage, or called "ZEROPAGE", or it was imported with .importzp, or it is otherwise a constant < 256.) If for some reason you have to define the variable on a line after the instruction, in that case you might want to put z: on it to let it know ahead of time, but if you make a habit of declaring variables at the top of the file it will just do it all automatically for you.
Re: Exposing Variable from CA65 to CC65
I see, thanks.
The "<" operator seems to be the only way to use direct-page addressing on page 1 when programming SPC700 with the SPC700 macro-pack. Ca65 really doesn't seem to like that you mark segments outside page 0 as zero-/direct-page. I guess the 65816 mode might have similar problems with direct-page addressing outside zero-page.
The "<" operator seems to be the only way to use direct-page addressing on page 1 when programming SPC700 with the SPC700 macro-pack. Ca65 really doesn't seem to like that you mark segments outside page 0 as zero-/direct-page. I guess the 65816 mode might have similar problems with direct-page addressing outside zero-page.
- rainwarrior
- Posts: 8006
- Joined: Sun Jan 22, 2012 12:03 pm
- Location: Canada
- Contact:
Re: Exposing Variable from CA65 to CC65
Yes, the assembler doesn't really know anything about direct page. That's a frequently requested thing.
Personally I haven't found it that much of a problem in practice, though. I've tried both of the following:
1. For variables are used exclusively in DP: put each page of DP in its own zero page segment. Ensure there's an equivalent space in memory reserved somewhere.
2. For variables that are used both ways: Make a set of DP aliases for variables that you want to access from both places. Something like:
You might make a convenient macro for the aliasing if that looks too ugly. Note that the aliases don't have to be declared in a segment since they're just assignments = and the assembler is smart enough to know from the < that they will always be 8 bit constants.
Personally I haven't found it that much of a problem in practice, though. I've tried both of the following:
1. For variables are used exclusively in DP: put each page of DP in its own zero page segment. Ensure there's an equivalent space in memory reserved somewhere.
2. For variables that are used both ways: Make a set of DP aliases for variables that you want to access from both places. Something like:
Code: Select all
.segment "RAM"
v1: .res 2
v2: .res 2
v3: .res 2
; direct page aliases
dvseg = v1
dv1 = <(v1 - dvseg)
dv2 = <(v2 - dvseg)
dv3 = <(v3 - dvseg)
; example
.segment "CODE"
lda #dvseg
tcd
lda dv1 ; should automatically be a DP instruction
lda z:dv1 ; if you want to be 100% certain it's a DP instruction
Last edited by rainwarrior on Mon Dec 07, 2020 4:39 pm, edited 1 time in total.
Re: Exposing Variable from CA65 to CC65
in asm file:
in c file:
Note that the first two bytes on page 0 are not occupied and may be ignored by the compiler.
In *.cfg file:
ZP: start = $0002, size = $0040, define = yes;
The start address tells the compiler how much space the zero page is actually allocated, because sometimes the first few bytes are used for purposes that the user does not know. It is recommended that the start address of zero page in your own cfg file should be greater than or equal to the original address in the cc65 template.
Similarly, the last a few addresses of 0 page space should not be used. cc65 may uses these as pseudo registers or other stack. It is also recommended not to allocate more than$80 $60.
In "nes.inc" file, cc65 itself takes up a lot of zero page space, as follows:
Code: Select all
.export _SELECTED_GAME
.segment "ZEROPAGE"
; zero page
_SELECTED_GAME: .res 2
in c file:
Code: Select all
extern s16 SELECTED_GAME;
#pragma zpsym("SELECTED_GAME")
In *.cfg file:
ZP: start = $0002, size = $0040, define = yes;
The start address tells the compiler how much space the zero page is actually allocated, because sometimes the first few bytes are used for purposes that the user does not know. It is recommended that the start address of zero page in your own cfg file should be greater than or equal to the original address in the cc65 template.
Similarly, the last a few addresses of 0 page space should not be used. cc65 may uses these as pseudo registers or other stack. It is also recommended not to allocate more than
In "nes.inc" file, cc65 itself takes up a lot of zero page space, as follows:
Code: Select all
;; FIXME: optimize zeropage usage
SCREEN_PTR = $62 ;2
CRAM_PTR = $64 ;2
CHARCOLOR = $66
BGCOLOR = $67
RVS = $68
CURS_X = $69
CURS_Y = $6a
tickcount = $6b ;2
VBLANK_FLAG = $70
ringbuff = $0200
ringwrite = $71
ringread = $72
ringcount = $73
ppuhi = $74
ppulo = $75
ppuval = $76
screenrows = (30-1)
charsperline = 32
xsize = charsperline