6502.org Forum  Projects  Code  Documents  Tools  Forum
It is currently Mon Apr 29, 2024 3:13 pm

All times are UTC




Post new topic Reply to topic  [ 19 posts ]  Go to page 1, 2  Next
Author Message
 Post subject: cc65 confusion...
PostPosted: Sun Mar 06, 2022 2:18 am 
Offline

Joined: Mon Feb 14, 2022 7:28 pm
Posts: 8
Hello!
First post here so I hope I'm doing things correctly..
I have the Ben Eater 6502 project done... mostly.
I'm using the cc65 tools and have managed to get printing to my LCD working.. sort of.
It handles printing a globally declared char string, but gets super weird printing a locally declared string.
I would expect the ','s to print, followed by the '!'s. Instead I get the ','s then garbage.
Any insight into what I'm doing wrong or misunderstanding about how all of this works would be greatly appreciated.

The following is my c code and then the generated assembly:

Code:
#include "ek6502.h"

unsigned char *command = ",,,,,,,,";   
int main() {
    unsigned char *c = "!!!!!!!!";

    lcdInit();
 
    lcdPrint(command);
   
    lcdPrint(c);
 
    return (0);
}

void handleInterrupt(void) {
}



Code:
;
; File generated by cc65 v 2.18 - Ubuntu 2.18-1
;
   .fopt      compiler,"cc65 v 2.18 - Ubuntu 2.18-1"
   .setcpu      "65SC02"
   .smart      on
   .autoimport   on
   .case      on
   .debuginfo   off
   .importzp   sp, sreg, regsave, regbank
   .importzp   tmp1, tmp2, tmp3, tmp4, ptr1, ptr2, ptr3, ptr4
   .macpack   longbranch
   .forceimport   __STARTUP__
   .import      _lcdInit
   .import      _lcdPrint
   .export      _handleInterrupt
   .export      _command
   .export      _main

.segment   "DATA"

_command:
   .addr   L0001

.segment   "RODATA"

L0004:
   .byte   $21,$21,$21,$21,$21,$21,$21,$21,$00
L0001:
   .byte   $2C,$2C,$2C,$2C,$2C,$2C,$2C,$2C,$00

; ---------------------------------------------------------------
; void __near__ __fastcall__ handleInterrupt (void)
; ---------------------------------------------------------------

.segment   "CODE"

.proc   _handleInterrupt: near

.segment   "CODE"

   rts

.endproc

; ---------------------------------------------------------------
; int __near__ main (void)
; ---------------------------------------------------------------

.segment   "CODE"

.proc   _main: near

.segment   "CODE"

   lda     #<(L0004)
   ldx     #>(L0004)
   jsr     pushax
   jsr     _lcdInit
   lda     _command
   ldx     _command+1
   jsr     _lcdPrint
   ldy     #$01
   jsr     ldaxysp
   jsr     _lcdPrint
   ldx     #$00
   lda     #$00
   jmp     L0003
L0003:   jsr     incsp2
   rts

.endproc



Top
 Profile  
Reply with quote  
 Post subject: Re: cc65 confusion...
PostPosted: Sun Mar 06, 2022 9:03 am 
Offline
User avatar

Joined: Thu Dec 11, 2008 1:28 pm
Posts: 10793
Location: England
Welcome!

What happens if you print from c first and then from command?


Top
 Profile  
Reply with quote  
 Post subject: Re: cc65 confusion...
PostPosted: Sun Mar 06, 2022 10:19 am 
Offline

Joined: Fri Apr 15, 2016 1:03 am
Posts: 136
Is the CC65 environment configuration appropriate for your system?
Are you running the CC65 environment initialization routine before calling main?

The global string pointer is defined globally. It doesn't use the CC65 local stack.
The local string pointer is defined on the CC65 local stack.

The CC65 local stack needs to be allocated in RAM . The zero-page pointer to the current location in it needs to be initialized & not overwritten.

RAM & Zero-page space allocations are specified in the CC65 environment configuration.
If you aren't already familiar with this, look at the sample .cfg files (none, apple2, etc) supplied with CC65.

I believe this info is correct, but I won't claim to be an expert on it.


Top
 Profile  
Reply with quote  
 Post subject: Re: cc65 confusion...
PostPosted: Sun Mar 06, 2022 9:10 pm 
Offline

Joined: Sun Nov 08, 2009 1:56 am
Posts: 387
Location: Minnesota
Well, the nice thing about having the generated assembly is that you can play with it to see if it does what you expect.

You can change this:

Code:
; ---------------------------------------------------------------
; int __near__ main (void)
; ---------------------------------------------------------------

.segment   "CODE"

.proc   _main: near

.segment   "CODE"

   lda     #<(L0004)
   ldx     #>(L0004)
   jsr     pushax
   jsr     _lcdInit
   lda     _command
   ldx     _command+1
   jsr     _lcdPrint
   ldy     #$01
   jsr     ldaxysp
   jsr     _lcdPrint
   ldx     #$00
   lda     #$00
   jmp     L0003
L0003:   jsr     incsp2
   rts

.endproc


to this:

Code:
; ---------------------------------------------------------------
; int __near__ main (void)
; ---------------------------------------------------------------

.segment   "CODE"

.proc   _main: near

.segment   "CODE"

;   lda     #<(L0004)
;   ldx     #>(L0004)
;   jsr     pushax

   jsr     _lcdInit

;   lda     _command
;   ldx     _command+1
 
   lda     #<L0001
   ldx     #>L0001
   jsr     _lcdPrint

;   ldy     #$01
;   jsr     ldaxysp

    lda    #<L0004
    ldx    #>L0004

   jsr     _lcdPrint
   ldx     #$00
   lda     #$00

;   jmp     L0003
; L0003:   jsr     incsp2
   rts

.endproc


....and see if that changes anything. If it does, that might give you a clue as to where things might be going wrong.

I'm assuming the compiler is doing what it does because of the initial assignment to the variable 'c'. That and not performing enough analysis to see that it doesn't have to do that first (or at all, really).

I wonder what it would do if you made the assignment closer to the point of actual use. Or used a string constant in the call instead of a variable. You might try replacing both variables with string constants to see what happens.


Top
 Profile  
Reply with quote  
 Post subject: Re: cc65 confusion...
PostPosted: Sun Mar 06, 2022 9:52 pm 
Offline
User avatar

Joined: Mon May 12, 2014 6:18 pm
Posts: 365
What happens when main returns? Just to be sure nothing weird is happening after that, you could try putting while(1); before return (0);. Also, no need to put parentheses around the value in a return statement.


Top
 Profile  
Reply with quote  
 Post subject: Re: cc65 confusion...
PostPosted: Mon Mar 07, 2022 5:45 am 
Offline

Joined: Mon Feb 14, 2022 7:28 pm
Posts: 8
BigEd wrote:
Welcome!

What happens if you print from c first and then from command?


Oh man, I've tried so many combinations that I honestly can't be sure, but I *think* it prints garbage then the good stuff


Top
 Profile  
Reply with quote  
 Post subject: Re: cc65 confusion...
PostPosted: Mon Mar 07, 2022 5:48 am 
Offline

Joined: Mon Feb 14, 2022 7:28 pm
Posts: 8
leepivonka wrote:
Is the CC65 environment configuration appropriate for your system?
Are you running the CC65 environment initialization routine before calling main?

The global string pointer is defined globally. It doesn't use the CC65 local stack.
The local string pointer is defined on the CC65 local stack.

The CC65 local stack needs to be allocated in RAM . The zero-page pointer to the current location in it needs to be initialized & not overwritten.

RAM & Zero-page space allocations are specified in the CC65 environment configuration.
If you aren't already familiar with this, look at the sample .cfg files (none, apple2, etc) supplied with CC65.

I believe this info is correct, but I won't claim to be an expert on it.


This sounds right. I'm SURE my config is wonky. I had a feeling this was the road I was about to go down. Perhaps trying to learn assembly AND the learn intricacies of the CC65 tools is too big of a bite.


Top
 Profile  
Reply with quote  
 Post subject: Re: cc65 confusion...
PostPosted: Mon Mar 07, 2022 5:49 am 
Offline

Joined: Mon Feb 14, 2022 7:28 pm
Posts: 8
teamtempest wrote:
Well, the nice thing about having the generated assembly is that you can play with it to see if it does what you expect.

You can change this:

Code:
; ---------------------------------------------------------------
; int __near__ main (void)
; ---------------------------------------------------------------

.segment   "CODE"

.proc   _main: near

.segment   "CODE"

   lda     #<(L0004)
   ldx     #>(L0004)
   jsr     pushax
   jsr     _lcdInit
   lda     _command
   ldx     _command+1
   jsr     _lcdPrint
   ldy     #$01
   jsr     ldaxysp
   jsr     _lcdPrint
   ldx     #$00
   lda     #$00
   jmp     L0003
L0003:   jsr     incsp2
   rts

.endproc


to this:

Code:
; ---------------------------------------------------------------
; int __near__ main (void)
; ---------------------------------------------------------------

.segment   "CODE"

.proc   _main: near

.segment   "CODE"

;   lda     #<(L0004)
;   ldx     #>(L0004)
;   jsr     pushax

   jsr     _lcdInit

;   lda     _command
;   ldx     _command+1
 
   lda     #<L0001
   ldx     #>L0001
   jsr     _lcdPrint

;   ldy     #$01
;   jsr     ldaxysp

    lda    #<L0004
    ldx    #>L0004

   jsr     _lcdPrint
   ldx     #$00
   lda     #$00

;   jmp     L0003
; L0003:   jsr     incsp2
   rts

.endproc


....and see if that changes anything. If it does, that might give you a clue as to where things might be going wrong.

I'm assuming the compiler is doing what it does because of the initial assignment to the variable 'c'. That and not performing enough analysis to see that it doesn't have to do that first (or at all, really).

I wonder what it would do if you made the assignment closer to the point of actual use. Or used a string constant in the call instead of a variable. You might try replacing both variables with string constants to see what happens.


excellent suggestion, I'll try it out.


Top
 Profile  
Reply with quote  
 Post subject: Re: cc65 confusion...
PostPosted: Mon Mar 07, 2022 5:53 am 
Offline

Joined: Mon Feb 14, 2022 7:28 pm
Posts: 8
Druzyek wrote:
What happens when main returns? Just to be sure nothing weird is happening after that, you could try putting while(1); before return (0);. Also, no need to put parentheses around the value in a return statement.


same results with or without an infinite loop.
Please forgive my C styling, 20 years of Java coding shows, huh?


Top
 Profile  
Reply with quote  
 Post subject: Re: cc65 confusion...
PostPosted: Mon Mar 07, 2022 12:59 pm 
Offline

Joined: Mon Sep 14, 2015 8:50 pm
Posts: 110
Location: Virginia USA
What’s the handleinterrupt function doing?

Normally, an irq or brk would return with an rti not rts.

Cheers,
Andy


Top
 Profile  
Reply with quote  
 Post subject: Re: cc65 confusion...
PostPosted: Mon Mar 07, 2022 5:37 pm 
Offline

Joined: Mon Feb 14, 2022 7:28 pm
Posts: 8
handyandy wrote:
What’s the handleinterrupt function doing?

Normally, an irq or brk would return with an rti not rts.

Cheers,
Andy


It's cruft, I was lazy, but thanks for the rti vs rts.


Top
 Profile  
Reply with quote  
 Post subject: Re: cc65 confusion...
PostPosted: Tue Mar 08, 2022 4:40 pm 
Offline

Joined: Sun May 13, 2018 5:49 pm
Posts: 247
[quote="echidna"]Hello!
Code:
#include "ek6502.h"

unsigned char *command = ",,,,,,,,";   
int main() {
    unsigned char *c = "!!!!!!!!";

    lcdInit();
 
    lcdPrint(command);
   
    lcdPrint(c);
 
    return (0);
}

void handleInterrupt(void) {
}


Your C code has some issues. The biggest issue is that you never actually allocate the memory for your strings. While you can get away with that (to some extent) in the global scope, you can't inside the function. Try the following change:
Code:
unsigned char command[] = ",,,,,,,,";
and inside main:
unsigned char c[] = "!!!!!!!!";

If cc65 doesn't like the empty []s, put one larger than the number of characters in your string (so there will be room for the null character on the end of the string, which the compiler will only add if there is room).
If you only want the strings to live in ROM, you can toss a "const" keyword in front of those declarations.

What you had before was only declaring pointers to character and then you pointed them to a constant string. The issue is that the constant string is an unnamed "temporary" value that only needs to be valid while the line is being evaluated. When you get into a local scope (eg. inside a function), these temporary values are often placed on the stack. This means your string of !s is placed on the stack and then that address is placed into the pointer. Once the code moves on to the next line, the compiler is not required to keep those temporary !s in memory. and the stack may be overwritten (especially with jsr instructions in the mix). The constant strings only needed to be there long enough for the assignment into the c pointer to take place.

It's a little more complicated than that, as the optimizer is optimizing away some of the operations, but the main point here is you actually need to store those strings in a variable (or constant) to have them guaranteed to still be around later. Changing your declarations to be an array of chars will guarantee the value exists beyond that one line of code.

You got lucky on the global scope because the compiler handles global variables a little differently and it "happened to work", but you should change that declaration as well.


Top
 Profile  
Reply with quote  
 Post subject: Re: cc65 confusion...
PostPosted: Tue Mar 08, 2022 5:38 pm 
Offline

Joined: Mon Feb 14, 2022 7:28 pm
Posts: 8
Thank you. This makes tons of sense.

SamCoVT wrote:
echidna wrote:
Hello!
Code:
#include "ek6502.h"

unsigned char *command = ",,,,,,,,";   
int main() {
    unsigned char *c = "!!!!!!!!";

    lcdInit();
 
    lcdPrint(command);
   
    lcdPrint(c);
 
    return (0);
}

void handleInterrupt(void) {
}


Your C code has some issues. The biggest issue is that you never actually allocate the memory for your strings. While you can get away with that (to some extent) in the global scope, you can't inside the function. Try the following change:
Code:
unsigned char command[] = ",,,,,,,,";
and inside main:
unsigned char c[] = "!!!!!!!!";

If cc65 doesn't like the empty []s, put one larger than the number of characters in your string (so there will be room for the null character on the end of the string, which the compiler will only add if there is room).
If you only want the strings to live in ROM, you can toss a "const" keyword in front of those declarations.

What you had before was only declaring pointers to character and then you pointed them to a constant string. The issue is that the constant string is an unnamed "temporary" value that only needs to be valid while the line is being evaluated. When you get into a local scope (eg. inside a function), these temporary values are often placed on the stack. This means your string of !s is placed on the stack and then that address is placed into the pointer. Once the code moves on to the next line, the compiler is not required to keep those temporary !s in memory. and the stack may be overwritten (especially with jsr instructions in the mix). The constant strings only needed to be there long enough for the assignment into the c pointer to take place.

It's a little more complicated than that, as the optimizer is optimizing away some of the operations, but the main point here is you actually need to store those strings in a variable (or constant) to have them guaranteed to still be around later. Changing your declarations to be an array of chars will guarantee the value exists beyond that one line of code.

You got lucky on the global scope because the compiler handles global variables a little differently and it "happened to work", but you should change that declaration as well.


Top
 Profile  
Reply with quote  
 Post subject: Re: cc65 confusion...
PostPosted: Tue Mar 08, 2022 6:44 pm 
Offline
User avatar

Joined: Sun Nov 27, 2011 12:03 pm
Posts: 229
Location: Amsterdam, Netherlands
SamCoVT wrote:
Your C code has some issues. The biggest issue is that you never actually allocate the memory for your strings. While you can get away with that (to some extent) in the global scope, you can't inside the function.

That is not true.

In general cases, a string constant (anything between double quotes) is statically allocated (clearly visible in the assembly code as well ...), i.e. it exists for the entire program run, and its type is either 'const char*' or 'char*' (depending on the implementation). There is no need here to copy it by value.


Top
 Profile  
Reply with quote  
 Post subject: Re: cc65 confusion...
PostPosted: Tue Mar 08, 2022 7:11 pm 
Offline
User avatar

Joined: Sun Nov 27, 2011 12:03 pm
Posts: 229
Location: Amsterdam, Netherlands
echidna wrote:
I would expect the ','s to print, followed by the '!'s. Instead I get the ','s then garbage.
Any insight into what I'm doing wrong or misunderstanding about how all of this works would be greatly appreciated.

My first guess would be that something in _lcdPrint corrupts the stack (and therefore the pointer to the string of plings, which is stacked at the time).


Top
 Profile  
Reply with quote  
Display posts from previous:  Sort by  
Post new topic Reply to topic  [ 19 posts ]  Go to page 1, 2  Next

All times are UTC


Who is online

Users browsing this forum: No registered users and 16 guests


You cannot post new topics in this forum
You cannot reply to topics in this forum
You cannot edit your posts in this forum
You cannot delete your posts in this forum
You cannot post attachments in this forum

Search for:
Jump to: