Help: CC65 Compiled Code Locking Up??

You need an actual VIC.

Moderator: Moderators

SparkyNZ
Vic 20 Enthusiast
Posts: 153
Joined: Tue Jan 18, 2011 2:23 am

Help: CC65 Compiled Code Locking Up??

Post by SparkyNZ »

Hi. I'm trying to have a play with doing some UDGs in C. I can't seem to get a simple 'POKE' loop to work though. All I'm trying to do is fill the @ char with pixels (turn it into a filled 8x8 square).

Why would CODE BLOCK #3 below fail to fill in all 8 bytes of the @??

I've even printed out all the values for 'a' in CODE BLOCK #3 without changing the 36869 and doing the assigning of the 0xFF and all 8 values print correctly until I do the assignining.

Am I doing something really stupid that I'm just not seeing or is there some CC65 quirk that I should be aware of??

Code: Select all

#include <stdio.h>

#define BYTE unsigned char
#define POKE(x,y) *(BYTE*)(x)=y

void main( void )
{
  int   c;
  BYTE *p;
  int   a;

  POKE( 36879, 8 );

  // Put a white @ char at the top of the screen
  printf( "\x8e\x13\x40\n" );
 
  POKE( 36869, 254 );

/* CODE BLOCK #1.. This works..
  POKE( 6144, 255 );
  POKE( 6145, 255 );
  POKE( 6146, 255 );
  POKE( 6147, 255 );
  POKE( 6148, 255 );
  POKE( 6149, 255 );
  POKE( 6150, 255 );
  POKE( 6151, 255 );
*/

/* CODE BLOCK #2..
  // This works too..
  for( a = 6144; a < 6152; a ++ )
  {
    //POKE( a, 0xFF );  Also works
    p = (BYTE*) a;
    *p = 0xFF;
  }
*/

/* CODE BLOCK #3.. DOES NOT WORK!!?? */
  for( c = 0; c < 8; c ++ )
  {
    a = 6144 + c;

    p = (BYTE*) a;
    *p = 0xFF;
  }

}
User avatar
Mike
Herr VC
Posts: 4841
Joined: Wed Dec 01, 2004 1:57 pm
Location: Munich, Germany
Occupation: electrical engineer

Post by Mike »

Most probably the program overwrites itself; 6144 is well located within the range occupied by the object code (~3K size and loaded either to $1000 or $1200).
User avatar
Kweepa
Vic 20 Scientist
Posts: 1315
Joined: Fri Jan 04, 2008 5:11 pm
Location: Austin, Texas
Occupation: Game maker

Post by Kweepa »

Yes, that's one of the disadvantages of the 8k/16k memory model. You have to move things around to use a larger screen or UDGs.

Note also that this produces extremely inefficient code (which you can see by adding -l to the command line before the input file name):

This is just for the final loop (with --static-locals and -O (optimize)):

Code: Select all

	lda     #$00
	sta     L0003
	sta     L0003+1
L000E:	lda     L0003
	cmp     #$08
	lda     L0003+1
	sbc     #$00
	bvc     L0015
	eor     #$80
L0015:	bpl     L000F
	lda     #$00
	clc
	adc     L0003
	pha
	lda     #$18
	adc     L0003+1
	tax
	pla
	sta     L0005
	stx     L0005+1
	sta     L0004
	stx     L0004+1
	sta     ptr1
	stx     ptr1+1
	lda     #$FF
	ldy     #$00
	sta     (ptr1),y
	lda     L0003
	ldx     L0003+1
	jsr     incax1
	sta     L0003
	stx     L0003+1
	jmp     L000E
It's mostly because you're using ints instead of chars where appropriate.

Here is a rewrite (note that by default cc65 considers chars unsigned):

Code: Select all

char c, *p = (char *)6144;
for (c = 0; c < 8; ++c) p[c] = 0xff;
Which looks like this:

Code: Select all

	lda     #$00
	sta     L0005
L000E:	lda     L0005
	cmp     #$08
	bcs     L000F
	lda     L0003
	ldx     L0003+1
	clc
	adc     L0005
	bcc     L0019
	inx
L0019:	sta     ptr1
	stx     ptr1+1
	lda     #$FF
	ldy     #$00
	sta     (ptr1),y
	inc     L0005
	jmp     L000E
Disappointing, actually. The manual says that p[c] should produce better code, but I don't see it.
That this is still pretty lame compared to some inline asm:

Code: Select all

   __asm__("lda #$ff");
   __asm__("ldx #8");
loop:
   __asm__("sta $17ff,x");
   __asm__("dex");
   __asm__("bne %g", loop);
Silly cc65.
SparkyNZ
Vic 20 Enthusiast
Posts: 153
Joined: Tue Jan 18, 2011 2:23 am

Post by SparkyNZ »

Thanks guys. The reason I ended up with int's etc was desperation. I began wondering if it was because I was adding BYTEs to int's etc thinking at first it was a casting problem.

I'd started messing around with a BASIC program for my unexpanded Vic with CBMPrgStudio and decided to move that to C. I'm not all that worried about the inefficiency at present as I'm just messing about but I work with C/C++ on a daily basis so its just easier for me to do things that way.

I didn't think to see where abouts the compiled code actually goes. I would have thought it would be smaller than a BASIC program but if it has a whole C runtime environment linked into it, it could be quite large?

I could try it with the 16k expansion I suppose. In all honesty thats what I'd intended anyway - Mike will be aware that I've just knocked up a DIY 16k expansion mod on my Vic - the main reason was to play around with hires graphics with double-height chars rather than on just a tiny screen subset with 3.5k.

So really, this is a Vic specific problem I've created rather than a CC65 compiler bug?

I reckon if I was to do anything serious I'd use assembly though. Not sure what my favourite environment would be for that yet but I'd probably play around in CBMPrgStudio first.
User avatar
Kweepa
Vic 20 Scientist
Posts: 1315
Joined: Fri Jan 04, 2008 5:11 pm
Location: Austin, Texas
Occupation: Game maker

Post by Kweepa »

Oh, if you're running in 3.5k, then you're probably out of memory. printf alone is 2k of library code, and the default stack is 1k.
SparkyNZ
Vic 20 Enthusiast
Posts: 153
Joined: Tue Jan 18, 2011 2:23 am

Post by SparkyNZ »

Kweepa wrote:Oh, if you're running in 3.5k, then you're probably out of memory. printf alone is 2k of library code, and the default stack is 1k.
Erm.. How do you set the program code origin? I've just switched to 16k and now I'm stumped as to where the compiler is loading the code. Can't see anything in the CC65 Vic 20 specific documentation about expanded memory code orgins etc.
User avatar
Kweepa
Vic 20 Scientist
Posts: 1315
Joined: Fri Jan 04, 2008 5:11 pm
Location: Austin, Texas
Occupation: Game maker

Post by Kweepa »

You have to edit the .cfg file that you pass to the cl65 command line with -C.
You can use the vic20-32k.cfg config that comes with cc65, and just change the RAM line in the MEMORY section:

Code: Select all

RAM: start = $11FF, size = $4E01, define = yes, file = %O;
This will use all available RAM.
SparkyNZ
Vic 20 Enthusiast
Posts: 153
Joined: Tue Jan 18, 2011 2:23 am

Post by SparkyNZ »

Kweepa wrote:You have to edit the .cfg file...
Excellent Kweepa! That did the trick. I just have .bat file like this and it works a treat. I should really be using a makefile but..

Code: Select all

cc65 -O -t vic20 paul.c
ca65 -t vic20 paul.s
ld65 -C vic20-16k.cfg -o paul paul.o vic20.lib
c1541 -attach Paul.d64 -delete paul -write paul paul
xvic -memory 16k Paul.d64
I'll have another go with the original problem in the morning. I know I'm going to have to have a read about the Vic memory allocation before I continue.

Is it possible to specify "data regions" when using CC65? In other words, can you specify regions of memory that should not be used by the code output? I don't know how the linker works but I guess code is just placed in one continuous block starting from the start address? I'd just like to know how to 'protect' a block of memory that I'll want to use for my graphics data.

Cheers
Paul
User avatar
Kweepa
Vic 20 Scientist
Posts: 1315
Joined: Fri Jan 04, 2008 5:11 pm
Location: Austin, Texas
Occupation: Game maker

Post by Kweepa »

Yes, that's quite possible.
I don't think cl65 will automatically try to fit your code around the empty blocks, but you can make it happen with some care.
Here's part of the .cfg file for Doom:

Code: Select all

MEMORY {
    ZP: start =  $0002, size = $001A, type = rw, define = yes;
    SLUTS: start = $400, size = $400, define = yes, file = "sluts";
    LOWCODEMEM: start = $800, size = $800, define = yes, file = "lowcode";
    STARTRAM: start = $11FF, size = $201, fill = yes, define = yes, file = %O;
    UDG: start = $1400, size = $400, fill = yes, file = %O;
    RAM: start = $1800, size = $6800, define = yes, file = %O;
    TEX: start = $A000, size = $D00, file = "textures";
    MAPMEM: start = $AD00, size = $A00, define = yes, file = "e1m1";
    MUSICMEM: start = $B700, size = $320, define = yes, file = "e1m1mus";
    SOUNDMEM: start = $BA20, size = $3E0, define = yes, file = "sounds";
}
SEGMENTS {
    STARTUP:  load = STARTRAM, type = ro;
    UDGS:     load = UDG, type = rw;
    TEXTURES: load = TEX, type = ro;
    LUTS:     load = SLUTS, type = ro;
    MAPDATA:  load = MAPMEM, type = ro;
    MUSIC:    load = MUSICMEM, type = ro;
	SOUNDS:   load = SOUNDMEM, type = ro;
	LOWCODE:  load = LOWCODEMEM, type = rw;
    INIT:     load = RAM, type = ro, define = yes, optional = yes;
    CODE:     load = RAM, type = ro;
    RODATA:   load = RAM, type = ro;
    DATA:     load = RAM, type = rw;
    ZPSAVE:   load = RAM, type = bss;
    BSS:      load = RAM, type = bss, define = yes;
    HEAP:     load = RAM, type = bss, optional = yes; # must sit just below stack
    ZEROPAGE: load = ZP,  type = zp;
}
I also have a second .cfg file for the levels that get loaded in.
User avatar
Mike
Herr VC
Posts: 4841
Joined: Wed Dec 01, 2004 1:57 pm
Location: Munich, Germany
Occupation: electrical engineer

Post by Mike »

SparkyNZ wrote:I'd just like to know how to 'protect' a block of memory that I'll want to use for my graphics data.
You could set the RAM configuration of cc65 for a BASIC start at $2001. This way all RAM from $1000 to $1FFF is usable for graphics data. A boot loader then shifts the BASIC start and chains to your executable:

Code: Select all

1 REM ** BOOT LOADER 
2 DN=PEEK(186) 
3 PRINT"{CLR}POKE43,1:POKE44,32:POKE8192,0:NEW" 
4 PRINT"{2 DOWN}LOAD"CHR$(34)"file name"CHR$(34)","DN 
5 POKE631,19:POKE632,13:POKE633,131:POKE198,3:END
... with the corresponding entry in the *.cfg file thus (for a +16K RAM expansion):

Code: Select all

RAM: start = $1FFF, size = $4001, define = yes, file = %O;
SparkyNZ
Vic 20 Enthusiast
Posts: 153
Joined: Tue Jan 18, 2011 2:23 am

Post by SparkyNZ »

Mike wrote:...BASIC start at $2001..

Code: Select all

RAM: start = $1FFF, size = $4001, define = yes, file = %O;
Hi Mike. Why are the start addresses out by 1 in each case? I would have thought that BASIC would be moved to something more round (or square! :) ) such as $2000.. which then makes me also question the $1FFF start for RAM.

Now, another question - not really related to the above question.. Assuming I'm using 16k, blocks 0 and 1. The ProgRefGuide leads me to think that block 0 starts at $2000 and block 1 starts at $4000, therefore residing between $2000-$5FFF. Is that correct?

The other book I have states that with 16k, screen memory starts at $1000. Is that because the normal 5k of Vic RAM is split as 1k for zero page and 4k starting at $1000?
User avatar
Kweepa
Vic 20 Scientist
Posts: 1315
Joined: Fri Jan 04, 2008 5:11 pm
Location: Austin, Texas
Occupation: Game maker

Post by Kweepa »

SparkyNZ wrote:Why are the start addresses out by 1 in each case?
I think it's just a quirk of the VIC. It's quite annoying.
The ProgRefGuide leads me to think that block 0 starts at $2000 and block 1 starts at $4000, therefore residing between $2000-$5FFF. Is that correct?
The blocks are 8k sections of the addressable memory numbered from 0-7, so a standard 16k expansion is blocks 1 and 2, at $2000 and $4000, so as you say, $2000-$5FFF.
Is that because the normal 5k of Vic RAM is split as 1k for zero page and 4k starting at $1000?
The zero page is just 256 bytes of RAM, but otherwise yes. A 3k expansion fills the memory hole between the lower 1k and the 4k starting at $1000.

http://www.zimmers.net/cbmpics/cbm/vic/memorymap.txt
User avatar
Mike
Herr VC
Posts: 4841
Joined: Wed Dec 01, 2004 1:57 pm
Location: Munich, Germany
Occupation: electrical engineer

Post by Mike »

SparkyNZ wrote:Why are the start addresses out by 1 in each case?
The byte before the BASIC start is always required to contain a zero. This is the reason why it often is located at some round address +1, for example $1001, $1201 or here, $2001.

When the project is linked, cl65 also includes the BASIC stub, the line in BASIC which starts the C program when RUN is typed. Part of the BASIC stub also creates the load address necessary for a *.prg file. These are two bytes before the program data in the file, but excluding the aforementioned 0 byte, i.e. the program is 'thought' to begin at that round address -1, i.e. $0FFF, $11FF or $1FFF for example.
Assuming I'm using 16k, blocks 0 and 1. The ProgRefGuide leads me to think that block 0 starts at $2000 and block 1 starts at $4000, therefore residing between $2000-$5FFF. Is that correct?
No, it's wrong. BLK1 := $2000 .. $3FFF, BLK2 := $4000 .. $5FFF. The range of $0000 .. $1FFF is sometimes referred to as BLK0, but as it is internal to the VIC-20 and no select signal appears on the expansion port, that usage is quite rare. Generally BLKn refers to the range n*$2000 to n*$2000+$1FFF.
The other book I have states that with 16k, screen memory starts at $1000. Is that because the normal 5k of Vic RAM is split as 1k for zero page and 4k starting at $1000?
The KERNAL prepares the screen memory start address this way, as soon as it finds RAM in BLK1. BASIC relies on a contigous RAM area, so in that case indeed the screen is shifted to the bottom of available RAM accessible to VIC on an unmodified VIC-20. Unfortunately, an external +3K expansion then is blocked from BASIC use (BLK1 takes precedence).

Edit: (also @kweepa) the bottom 1K are sometimes referred to as 'system area', but as kweepa wrote, only the first 256 bytes are the zeropage.
Last edited by Mike on Tue Jan 03, 2012 3:05 pm, edited 1 time in total.
SparkyNZ
Vic 20 Enthusiast
Posts: 153
Joined: Tue Jan 18, 2011 2:23 am

Post by SparkyNZ »

Mike wrote:No, it's wrong. BLK1 := $2000 .. $3FFF, BLK2 := $4000 .. $5FFF.
Yeah just checked the RAM expansion mod notes - it is blocks 1 and 2 I'm using, sorry. I thought it was 0 and 1 for some reason, so at least that sounds consistent with what you guys are saying.

Well.. the kids are back so I'll have to put all of this on hold again for a while. Thanks for your help again guys! :)
User avatar
Kweepa
Vic 20 Scientist
Posts: 1315
Joined: Fri Jan 04, 2008 5:11 pm
Location: Austin, Texas
Occupation: Game maker

Post by Kweepa »

Ok, I managed to get cc65 to compile a single prg that reserves space for UDGs at $1400-$1C00. Just change your cfg file to read as follows:

Code: Select all

MEMORY {
    ZP: start =  $0002, size = $001A, type = rw, define = yes;
    RAM: start = $11FF, size = $A01, define = yes, fill = yes, file = %O;
    URAM: start = $1C00, size = $4400, define = yes, file = %O;
}
SEGMENTS {
    STARTUP:  load = RAM, type = ro;
    LOWCODE:  load = RAM, type = ro,               optional = yes;
    INIT:     load = RAM, type = ro, define = yes, optional = yes;
    CODE:     load = URAM, type = ro;
    RODATA:   load = URAM, type = ro;
    DATA:     load = URAM, type = rw;
    ZPSAVE:   load = URAM, type = bss;
    BSS:      load = URAM, type = bss, define = yes;
    HEAP:     load = URAM, type = bss, optional = yes; # must sit just below stack
    ZEROPAGE: load = ZP,  type = zp;
}
Note the "fill = yes" for RAM. This fills the output file with zeroes for any unused space in the RAM section.

It's a little bit wasteful if you're not using an expanded screen size, but oh well. With some care I'm sure the gaps could be filled.
Post Reply