WDC02CC Compiler

Programming the 6502 microprocessor and its relatives in assembly and other languages.
Andre
Posts: 21
Joined: 03 May 2021
Location: Brisbane Australia

Re: WDC02CC Compiler

Post by Andre »

Hi again,

Hardware is built and has some code running. (only 1 hardware error so far...no pull-up on BE pin)
Here is some feedback on the compiler.

With no UART or screen/keyboard the only hello world is via a LED connected to to a register that controls memory bank switching.
The register is at 0xBF00, in assembler the code to write to this register would be

lda #192
sta $BF00

In the WDC Tools W65C02S C COMPILER/OPTIMIZER USER GUIDE, page 35, under Referencing I/O there are some examples of doing this in C, but the target appears to be the W65C816S.

In C one could do something like this,

volatile unsigned char *reg1_write = (unsigned char *)0xBF00;

*reg1_write = 0x80;

I ran into trouble with the make file, specifically this line when linking

WDCLN -cE000 -d300 -sz -hi -g -t -o main.hex STARTUP02.obj main.obj -lc

The -cE000 tells the linker where to place the CODE in ROM
The -d300 tells the linker where to place the DATA in RAM/ROM

See WDC Tools W65C02S C COMPILER/OPTIMIZER USER GUIDE, page 13, Creating A ROM Program
Also ASSEMBLER/LINKER/LIBRARIAN user manual, page 35
for more details

The resulting Intel HEX file looks like this, (I've cut it back to safe space)

:0603000000D800FF00BF61
:18E000002018E000000000AD04038568AD05038569A98092684C07E056
...
:18FF4800390538F020A906853CA903853DA900A898913CE63CD002E6AD
:18FF60003DA538D004C6393004C63880EB2000E00000FFFFFFFFFFFF05
...
:10FFF000FFFFFFFFFFFFFFFFFFFF000000FF00000C
:00000001FF

The first line is the problem. These constants (and my BF00) should be in ROM from where the startup code will copy them to RAM. (not at 0300)
It took me a while to get my head around this. I even switched to using assembler just to get my first lines of code running. I inlined the above to assembler lines in the C program.

The linker line should look like this.

WDCLN -cE000 -d300, -sz -hi -g -t -o main.hex STARTUP02.obj main.obj -lc

Notice just the coma has been added. The manuals explain this (well) and though I've read it a hundred times I don't get the feeling that I missing something.

With the modified linker line the HEX file now looks like this,

:18E000002018E000000000AD04038568AD05038569A98092684C07E056
...
:18E0F000A0070878B13C9930008810F828A0018A6C300000D800FF00E5
:01E10800BF57
:18FF0000D8A2FF9AA9008532A9028533A9068538A90085390538F029EA
...
:18FFD800FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF29
:10FFF000FFFFFFFFFFFFFFFFFFFF000000FF00000C
:00000001FF

now my BF00 is in the light location, following the CODE section.

Something else that I had to compensate for, notice that the HEX file address is at E000 which is correct.
But when loading this HEX file into the programmer it loads at E000 and onward. I don't want it there. I want it at 0000.
Fortunately the programmer (TL-8662-plus) has an option to relocate the file a specified start address.

It may(or not) be possible to do this from the linker line above. I was unable get it to work.

For interest the C code
*reg1_write = 0x80;
translates to
43 00:0007: AD xx xx lda _reg1_write
44 00:000A: 85 68 sta <104
45 00:000C: AD xx xx lda _reg1_write+1
46 00:000F: 85 69 sta <105
47 00:0011: A9 80 lda #128
48 00:0013: 92 68 sta (104)

If I was going to use a structure like this to initialize a 6551 for example then some inline assembler like
lda #192
sta $BF00
would be more efficient.

It's been a very long time since I've used an "EPROM" programmer and it's the first time I'm bringing up a 6502 so if I've missed anything please fell free to correct or add.

Next step is a 6551 UART....how would we survive without a UART for debugging...and software downloads. (Oh yes....It's called an ISP)

Regards
Andre
User avatar
Agumander
Posts: 129
Joined: 17 Jul 2018
Location: Long Island, NY
Contact:

Re: WDC02CC Compiler

Post by Agumander »

Regarding the addressing of the output file, I have noticed that it seems to output an entire memory space's worth of data even if your ROM portion is only 8K. I've taken to using the "dd" utility to cut out the ROM portion of that output file and included that action in my build script.
Andre
Posts: 21
Joined: 03 May 2021
Location: Brisbane Australia

Re: WDC02CC Compiler

Post by Andre »

Hi

I now have both WDC and CC65 working. I must say that CC65 is a decent piece of kit. (Thanks to those that made it happen)

A major problem with the WDC compiler is the lack of error message detail.
For example here is the output with an error in the code,

D:\Software\6502\RETRO1>WDC02CC -bs -lt -DUSING_02 main.c
WDC 65C02 C Version 3.49.1 Feb 6 2006 16:25:18
Copyright 1992-2006 by The Western Design Center, Inc.
{

no line number, just "{". Any ideas what this means? A missing "{" bracket maybe.
The error is a missing function prototype.

As Druzyek pointed out in another post, CC65 produces more efficient code.
I compared some code that I have been working on and found that
WDC comes in at 1922 bytes (CODE)
CC65 comes in at 1860 bytes (CODE)
In fact CC65 does even better as I had to remove the UTOA function from WDC as it was not available.
This is not a huge difference, but when coding in C for the 6502 you do want to save every byte possible.

For now I am going to ditch the WDC compiler and work with CC65.

Regards
Andre
User avatar
Druzyek
Posts: 367
Joined: 12 May 2014
Contact:

Re: WDC02CC Compiler

Post by Druzyek »

Quote:
no line number, just "{". Any ideas what this means? A missing "{" bracket maybe.
The error is a missing function prototype.
It seems to be showing the line before the error instead of the line of the error. I though about setting up something that could do basic syntax checking in C when I was interested in using WDC. I think a lint type program may help, but I've never used one before.
BillG
Posts: 710
Joined: 12 Mar 2020
Location: North Tejas

Re: WDC02CC Compiler

Post by BillG »

Andre wrote:
no line number, just "{". Any ideas what this means? A missing "{" bracket maybe.
The error is a missing function prototype.
Could it be that "{" is the first character/token after the error has been detected?

For example,

Code: Select all

void foo(int bar)
{
}
instead of the expected

Code: Select all

void foo(int bar);
void foo(int bar)
{
}
Try

Code: Select all

void foo(int bar);
void foo(int bar)
[
]
to see if you get just "["
User avatar
Druzyek
Posts: 367
Joined: 12 May 2014
Contact:

Re: WDC02CC Compiler

Post by Druzyek »

Quote:
Could it be that "{" is the first character/token after the error has been detected?
Yes, I think that's right.
Andre
Posts: 21
Joined: 03 May 2021
Location: Brisbane Australia

Re: WDC02CC Compiler

Post by Andre »

Hi,

It shows the line after the error, for example

D:\Software\6502\RETRO1>WDC02CC -bs -lt -DUSING_02 main.c
WDC 65C02 C Version 3.49.1 Feb 6 2006 16:25:18
Copyright 1992-2006 by The Western Design Center, Inc.
unsigned char ACIA_buffer_full(void);

and here is the code

void ACIA_Print(char *data)
unsigned char ACIA_buffer_full(void);

The error is the missing semi colon for the prototype.

This is the problem that I had

void putch(unsigned char b);
void ACIA_Print(char *data);
//unsigned char ACIA_buffer_full(void);
unsigned char getch(void);

unsigned char byte;
char WrkSpc[40];

#define KEY_BUFFER_SIZE 32
unsigned char key_buffer[KEY_BUFFER_SIZE];
unsigned char key_buffer_index = 0;
unsigned char terminal_command_ready = 0;

#define MAX_TERMINAL_COMMAND_PARAM 4
char *t[MAX_TERMINAL_COMMAND_PARAM];

unsigned int address;
unsigned char byte_at_address;

void main(void)
{
....rest of the program follows

Notice that I have commented out the function "ACIA_buffer_full" above. It is a function that this program uses.

D:\Software\6502\RETRO1>WDC02CC -bs -lt -DUSING_02 main.c
WDC 65C02 C Version 3.49.1 Feb 6 2006 16:25:18
Copyright 1992-2006 by The Western Design Center, Inc.
{

when compiling I get the above output.
The compiler is unhappy that I have a missing function prototype.
Uncomment the function and it's happy.

I suspect the compiler output is referring to this "{", where the function is created, indicating the error is on or related to the previous line

unsigned char ACIA_buffer_full(void)
{
...

But since there could be many functions in my program which one is it referring to.
A line number would help and perhaps a description of the error.

Regards
Andre
User avatar
aleferri
Posts: 21
Joined: 02 May 2021

Re: WDC02CC Compiler

Post by aleferri »

Code: Select all

As Druzyek pointed out in another post, CC65 produces more efficient code.
I compared some code that I have been working on and found that
WDC comes in at 1922 bytes (CODE)
CC65 comes in at 1860 bytes (CODE)
In fact CC65 does even better as I had to remove the UTOA function from WDC as it was not available.
This is not a huge difference, but when coding in C for the 6502 you do want to save every byte possible.

For now I am going to ditch the WDC compiler and work with CC65.
Reading the listing of cc65 output doesn't seem more efficient, it is more compact, but it uses a large amount of procedures to keep the output small.
User avatar
enso
Posts: 904
Joined: 29 Sep 2012

Re: WDC02CC Compiler

Post by enso »

Andre wrote:
*reg1_write = 0x80;
translates to
43 00:0007: AD xx xx lda _reg1_write
44 00:000A: 85 68 sta <104
45 00:000C: AD xx xx lda _reg1_write+1
46 00:000F: 85 69 sta <105
47 00:0011: A9 80 lda #128
48 00:0013: 92 68 sta (104)

If I was going to use a structure like this to initialize a 6551 for example then some inline assembler like
lda #192
sta $BF00
would be more efficient.
...
The compiler sees reg1_write as a pointer, but is not a constant, so it treats it as a variable, as it must (because it may be assigned later, possibly from a different compilation unit). The actual store code, after the global initialization, is one byte shorter...

Has anyone tried declaring it as a volatile unsigned char * const? Then it should not wind up using up the zero page. I am curious what 6502 compilers do with consts. If anyone with a compiler is willing to try, please post results.

As much as I enjoy trash-talking C compilers, this one is compiling good code - it just cannot read people's minds.
In theory, there is no difference between theory and practice. In practice, there is. ...Jan van de Snepscheut
User avatar
Druzyek
Posts: 367
Joined: 12 May 2014
Contact:

Re: WDC02CC Compiler

Post by Druzyek »

Quote:
As much as I enjoy trash-talking C compilers, this one is compiling good code - it just cannot read people's minds.
How do you have reg1_write defined? If you want the memory that it points to be modifiable but the address to not be modifiable you can use something like "unsigned char * const reg1_write"

Neither CC65 nor WDC are smart enough to convert *reg1_write=0x23; into LDA #$23 then STA $BF00:

Code: Select all

CC65:
ldx     #$BF
lda     #$00
jsr     pushax
jsr     ldax0sp
sta     ptr1
stx     ptr1+1
lda     #$23
sta     (ptr1)

Code: Select all

WDC:
;ldx #0 ;performed during startup
stx	<64
lda	#191
sta	<65
lda	#35
sta	(64)
Just as an example, gcc for the MSP430 has no problem turning it into one instruction that writes the constant 0x23 to 0xBF00 since the compiler does have enough information to skip the pointer and generate good assembly.

In case you haven't seen it, godbolt.org is a really useful website to quickly see what assembly different compilers output for a particular C snippet. It even has CC65 on there.
BillG
Posts: 710
Joined: 12 Mar 2020
Location: North Tejas

Re: WDC02CC Compiler

Post by BillG »

Druzyek wrote:
How do you have reg1_write defined? If you want the memory that it points to be modifiable but the address to not be modifiable you can use something like "unsigned char * const reg1_write"
From the first post at the top of this page:
Andre wrote:
volatile unsigned char *reg1_write = (unsigned char *)0xBF00;
User avatar
enso
Posts: 904
Joined: 29 Sep 2012

Re: WDC02CC Compiler

Post by enso »

Could someone try

Code: Select all

volatile unsigned char * const reg1_write = ...
In theory, there is no difference between theory and practice. In practice, there is. ...Jan van de Snepscheut
mysterymath
Posts: 79
Joined: 10 Jan 2021

Re: WDC02CC Compiler

Post by mysterymath »

Just for fun I ran this through LLVM-MOS; without const, I get:

Code: Select all

lda     #128
ldx     reg1_write
ldy     reg1_write+1
stx     mos8(__rc2)
sty     mos8(__rc3)
ldy     #0
sta     (mos8(__rc2)),y
With const, I get:

Code: Select all

lda     #128
sta     48896
EDIT: You also get this if you say "static", since the compiler can see that the symbol isn't written any other values. And it looks like zero-page-indirect version only occurred because I was compiling only one file to ASM with clang -S; when I actually used the end-to-end LTO configuration to make a binary for the c64, it had the LDA/STA version; it could see that all accesses to that symbol program-wide were in that one file, so the LTO system must've made it static automatically (IIRC, they call this "internalization").
User avatar
enso
Posts: 904
Joined: 29 Sep 2012

Re: WDC02CC Compiler

Post by enso »

Right. Static variables have file scope; the compiler can clearly see if there is any attempt to write to the pointer after initialization. If not, it is not an lvalue, and does not need storage space.

Similarly, any global compiler setting that guarantees locality allows the compiler to optimize the pointer away.

The const declaration allows you to have truly global pointers while making read-only guarantees to the compiler.

Thank you. Hopefully others will try it with WDC and CC65.
In theory, there is no difference between theory and practice. In practice, there is. ...Jan van de Snepscheut
User avatar
Druzyek
Posts: 367
Joined: 12 May 2014
Contact:

Re: WDC02CC Compiler

Post by Druzyek »

Quote:
Hopefully others will try it with WDC and CC65.
The code with static is pretty similar to what you get with const. They both set up a pointer to a fixed address and write to it rather than optimizing the pointer out.
Post Reply