Code Monkey home page Code Monkey logo

cc65's Introduction

About cc65

cc65 is a complete cross development package for 65(C)02 systems, including a powerful macro assembler, a C compiler, linker, archiver and several other tools. cc65 has C and runtime library support for many of the old 6502 machines. For details look at the Website.

People

Project founders:

  • John R. Dunning: original implementation of the C compiler and runtime library, Atari hosted
  • Ullrich von Bassewitz:
    • move the code to modern systems
    • rewrite most parts of the compiler
    • complete rewrite of the runtime library

Core team members:

External contributors:

(The above list is incomplete, if you feel left out - please speak up or add yourself in a PR)

For a complete list look at the full team list or the list of all contributors

Contact

For general discussion, questions, etc subscribe to the mailing list or use the github discussions.

Some of us may also be around on IRC #cc65 on libera.chat

Documentation

  • The main Documentation for users and developers

  • Info on Contributing to the CC65 project. Please read this before working on something you want to contribute, and before reporting bugs.

  • The Wiki contains some extra info that does not fit into the regular documentation.

Downloads

Snapshot Build

cc65's People

Contributors

acqn avatar an-s avatar bbbradsmith avatar blackystardust avatar brazso avatar clbr avatar colinleroy avatar compyx avatar fabrizio-caruso avatar greg-king5 avatar groessler avatar irgendwera8 avatar jedeoric avatar jmr avatar karrika avatar laubzega avatar marianodominguez avatar movax12 avatar mrdudz avatar oliverschmidt avatar ops avatar pfusik avatar polluks avatar polluks2 avatar ppelleti avatar rofl0r avatar smuehlst avatar svenmichaelklose avatar vrubleg avatar wayneparham avatar

Stargazers

 avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar

Watchers

 avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar

cc65's Issues

extern/static conflict

extern int xx;
static int xx;

int main()
{
return xx;
}

gcc4 says:
a.c:2: error: static declaration of 'xx' follows non-static declaration
a.c:1: note: previous declaration of 'xx' was here

import/export of zeropage symbols using pragmas in C does not work as expected

what i did was exporting a symbol in one C file, using pragmas to make it a zeropage symbol and also pushing it into a custom segment. and then i import it in another file, again using pragma to make it a zeropage symbol (as described in the docs):

extern char foo;
#pragma zpsym ("foo")
int n;
int main(void) {
    n = foo;
    return 0;
}
//#pragma bss-name (push,"ZEROPAGE")            /* no warning */
#pragma bss-name (push,"MYZEROPAGE")            /* warning */
char foo;
#pragma zpsym ("foo")
#pragma bss-name (pop)

now the odd thing is, the assembler gives this warning:

zpsym2.s(18): Warning: Symbol `foo' is absolute but exported zeropage

...which seems wrong (and is a bit irritating). also the generated code is correct (uses zeropage adressing).

whats interesting is that the warning goes away when putting the symbol into the regular "ZEROPAGE" segment (without changing anything else) - that seems to imply that its actually a bug, because for extra zeropage variables using an extra segment is pretty much required.

setjmp shorter

--- ./libsrc/common/setjmp.s.bak        2015-06-25 08:19:35 +0000
+++ ./libsrc/common/setjmp.s    2015-08-05 00:57:35 +0000
@@ -6,6 +6,7 @@

         .export         __setjmp
         .importzp       sp, ptr1
+        .import         return0

 __setjmp:
         sta     ptr1            ; Save buf
@@ -44,9 +45,4 @@

 ; Return zero

-        lda     #0
-        tax
-        rts
-
-
-
+        jmp     return0

Inconsistent result on divide with a negative numerator and a constant or variable denominator

When dividing a negative integer numerator by a constant number, or by a variable containing that same number, the compiler generates different results.

This sample code illustrates, and will erroneously print "Not Equal":

#include <stdio.h>
void main(void)
{
    int a = -1, b = 8;
    if ((a/8) == (a/b))
        printf("Equal\n");
    else
        printf("Not Equal\n");
}

Looking at the generated code, it's easy to see where the error lies. The compiler substituted a shift operation for the divide-by-a-constant. The shift operation doesn't yield the same answer as the divide if the numerator is negative.
For the case above, -1 >> 3 = -1 and -1 / 8 = 0.

;
; if ((a/8) == (a/b))
;
    ldy     #$03
    jsr     ldaxysp
    jsr     asrax3      ; use >> 3 to divide by 8
    jsr     pushax
    ldy     #$05
    jsr     ldaxysp
    jsr     pushax
    ldy     #$05
    jsr     ldaxysp
    jsr     tosdivax    ; use divide code to divide by 8

The optimizer switched to using asrax3 for the constant division; and, used tosdivax for the division by the variable, resulting in -1/8 not giving a consistent result, depending on whether the 8 is used as a literal or in a variable.

No BLT mnemonic when using 65816 mode

This page claims that BLT is accepted as a synonym for BCC when in 65816 mode. This doesn't appear to be the case, at least of the latest Windows snapshot; I get "Error: ':' expected" when I try to use it, even though I know for a fact that this code is assembling in 65816 mode.

I assume the same is true of the other instructions in the list, but BLT was the only one I've tested so far.

Amiga support

Amiga support is broken since the new makefiles. Anyway you can simply control it inside the source:

--- ./src/cl65/main.c.bak 2015-06-25 08:19:35 +0000
+++ ./src/cl65/main.c 2015-06-26 01:29:55 +0000
@@ -148,7 +148,7 @@

#if defined(NEED_SPAWN)
-# if defined(SPAWN_AMIGA)
+# if defined(_AMIGA)

include "spawn-amiga.inc"

else

include "spawn-unix.inc"

Bug shifting right

Hello.

I've been a cc65 user for quite a long time. I've been using 2.13.3 but today I decided to, finally, upgrade to the latest version, but I found something: This code:

unsigned char uc;
signed int si;

...

void main (void) {
    [...]
    si = 160 << 6;
    uc = si >> 6;
    [...]
}

"pry" should equal 160, but the result is 224 instead with the latest "win32 snapshot". I was afraid this was an error on my side, but I double checked with some other values and asked other users in nesdev in this thread.

http://forums.nesdev.com/viewtopic.php?f=2&t=13616&p=160508#p160508

It seems that I found a bug, as the user lidnariq mentions:

In the older release, the >>6 generates calls to asrax4 and asrax2. In the newer one, it instead generates a call to just asrax4 and then tries to inline the remaining >>2, but it does it wrong (it forgets about X and sign-extends A instead of copying the two remaining bits from X).

Thank you very much.

Missing common.a

I tried compiling cc65 for AmigaOS 4.1 but for some reason make script doesn't produce 'common.a' file and compiling fails:

gcc: ../wrk/common/common.a: No such file or directory
make[1]: *** [../bin/ar65] Error 1
make: *** [all] Error 2

In the 'wrk/common' directory there are .o and .d files but no .a files.
I'm using gcc version 4.2.4. For compiling I entered the following shell command (in the 'cc65' directory):

make USER_CFLAGS=-DSPAWN_AMIGA=1

some samples fail to compile

some of the programs in the samples directory do not compile (for the default target, which is used by the makefile)

$ make --keep-going                                                                                                    
ascii.c                                                                                                                                                                            
diodemo.c                                                                                                                                                                          
Unresolved external `_dio_close' referenced in:                                                                                                                                    
  diodemo.s(1353)                                                                                                                                                                  
  diodemo.s(1363)                                                                                                                                                                  
Unresolved external `_dio_open' referenced in:                                                                                                                                     
  diodemo.s(747)                                                                                                                                                                   
  diodemo.s(1053)                                                                                                                                                                  
Unresolved external `_dio_query_sectcount' referenced in:                                                                                                                          
  diodemo.s(800)                                                                                                                                                                   
  diodemo.s(1111)                                                                                                                                                                  
Unresolved external `_dio_query_sectsize' referenced in:                                                                                                                           
  diodemo.s(788)                                                                                                                                                                   
  diodemo.s(1094)                                                                                                                                                                  
Unresolved external `_dio_read' referenced in:
  diodemo.s(976)
Unresolved external `_dio_write' referenced in:
  diodemo.s(1250)
ld65: Error: 6 unresolved external(s) found - cannot create output file
Makefile:84: recipe for target 'diodemo' failed
make: *** [diodemo] Error 1
enumdevdir.c
fire.c
gunzip65.c
hello.c
mandelbrot.c
mousetest.c
make: *** No rule to make target 'multdemo', needed by 'all'.
nachtm.c
make: *** No rule to make target 'ovrldemo', needed by 'all'.
plasma.c
sieve.c
tgidemo.c
make: Target 'all' not remade because of errors.

... when this is fixed, may i suggest that the samples dir is included in "make all", so future API breakage will be noticed immediately?

__asm__ volatile

Hello, I know that cc65 doesn't support volatile variables.
But It can be very helpful to write NES games using cc65.
I'm trying to write a little api for NES with assembly and C.

Is there a way I can get something like asm volatile ?
I have strong background in C and C++. and learning 6502 assembly
if someone guide me in the right direction I could try to implement that in the compiler.
But I've no experience in compiler source code yet.
Thanks.

Linker problem with set_irq

I wrote the following short program to test interrupts on Plus/4:

#include <6502.h>
#include <plus4.h>

#define TEMP_STACK_SIZE 2048

void main ( void );
unsigned char f_IntRoutine ( void );

unsigned char aucTempStack [ TEMP_STACK_SIZE ];

void main ( void ) {
    SEI ();
    set_irq (
        &f_IntRoutine,
        aucTempStack, TEMP_STACK_SIZE
    );
    CLI ();

    for ( ;; ) { }
}

unsigned char f_IntRoutine ( void ) {
    ++TED.bordercolor;

    return ( IRQ_NOT_HANDLED );
}

When I compile and link the program with cl65, I get these two warnings:

d65: Warning: cc65:cfg/plus4.cfg(22): Segment BSS' with typebss' contains initialized data

ld65: Warning: Address size mismatch for __ZP_START__': Exported from cc65:cfg/plus4.cfg(7) asabsolute', import in interrupt.o, common/interrupt.s(11) as `zeropage'

If I run the program, it doesn't work: Plus/4 encounters a break instruction which stops the execution.

The target system is 'plus4' and I'm using the default linker configuration file for the Plus/4.

Wrong implicit conversion of integers

While the standard says that an unsigned char should be promoted to int if an int can represent all values of an unsigned char, and similar for promotion from unsigned to long, cc65 promotes unsigned types to unsigned types of the larger range. That is, unsigned char is promoted to unsigned, and unsigned to unsigned long.

This can be easily tested. The following code should promote the operands to integer and produce signed results; but, if you look at the generated assembly, it uses the runtime routines for unsigneds, which generate unsigned results:

int main(void)
{
    unsigned char c = 2;
    unsigned u = 2;
    int a = -2;
    long l = -2;

    /* Generated code should use tosmulax but uses tosumulax */
    int r = c * a;
    /* Generated code should use tosmuleax but uses tosumuleax */
    long lr = u * l;

    return 0;
}

While, in the examples above, that has no ill effects, and is therefore only visible by looking at the generated code, there are other places where the compiler gets tests wrong, or produces invalid calculation results. For example:

long n = -95;
unsigned int d = 3;
int r = n/d; // produces 21813 instead of 31

A workaround is to manually force usage of the correct type:

int r = n / (long) d;

The disadvantage being that one must really understand the nature of the bug to apply the proper casts.

A fix is possible with not too much effort (needs rewrite of two functions in the compiler), but introduces two other problems:

The fixed code has a higher chance to trigger an error in the optimizer which causes invalid code transformations. This optimizer problem does actually exist without the fix, but chances for triggering it are rather low.
Fixing the conversions will have a negative impact on code quality. The reason is the integer promotions: The C standard says that operands to most binary operators must be converted to int. If that conversion is done to signed instead of unsigned, most operations on char will actually operate on signed ints. And on 6502 machines, code for a signed operation is usually larger and slower than the same code for an unsigned operation.

So, if you have plans to solve the issue, be at least prepared to have a thorough look at the generated code.

ca65: Can't use negative values with .byte and .word

Code that used to assemble and run just fine with older (pre-github) versions of ca65 now break because .byte and .word can't accept negative values anymore. Trying it raises error messages such as "Error: Range error (-1020 not in [0..65535])".

This is using the latest Windows snapshot.

using "static" to declare forward reference doesnt work

this basically describes what should happen: http://infocenter.arm.com/help/topic/com.arm.doc.faqs/ka9772.html

in my code i have something like this:

 typedef struct _DIRMENU
 {
     char *name;
     struct _DIRMENU *dest;
 } DIRMENU; 

 extern DIRMENU rmenu; /* <-- this currently "works" but gives a warning, GCC gives an error */
 /* static DIRMENU rmenu; */ /* <-- this SHOULD work, but gives an error in cc65 */

 static DIRMENU lmenu = {
     "left",
     &rmenu
 };

 static DIRMENU rmenu = {
     "right",
     &lmenu
 };

README.md has incorrect markup for Apple ][ line

A literal was missed causing (text) editors to markup incorrect syntax because they think the line Apple ][ has a begin tag.

To fix, replace the line:

- the Apple ][+ and successors.

with

- the Apple ]\[+ and successors.

Wishlist: static register

As you know, register has some set-up overhead compared to function-static variables. Many functions tend to have basic loop variables i, j, k and so on, and global ones in the ZP can't be used, since they can call each other.

One can manually put funcA_i, funcB_i globally to the zero page, but that makes the code really ugly.

So I was thinking, how about an extension that would allow "static register unsigned char i"? It would be a global zeropage variable, mangled so that it's unique to that function, but without making the code ugly.

Good idea?

Documentation of calling conventions and requirements

I'm trying to find documentation of the calling conventions required for interaction between assembly and C code, but it doesn't seem to be explicitly documented anywhere.

There is a little bit about fastcall vs cdecl convention here, but it seems very incomplete:
http://cc65.github.io/doc/cc65.html#s5

In particular, there seems to be an unspoken requirement for the caller to set X to 0 if it returns an unsigned char type?
#263

That last one really surprised me; and I don't think it's something you'd easily catch from looking at the assembled call; it's just waiting for the right/wrong situation to manifest as a difficult bug. I think it would be very helpful to have this explicitly documented so it doesn't become a matter of finding these things out by accidentally failing, or having to study the compiler intensely.

If this all exists in the documentation already and I just couldn't find it, I apologize in advance. I did look for it before posting here.

So, here are some questions that I'd hope the documentation would address:

  1. Requirements of the called function, and return values.

Which registers, A X or Y are the responsibility of the function? Are there any other states that it is responsible for? Does the responsibility depend entirely on the return type of the function, or are there other conditions?

If it returns void, does the function have any special return requirements, or will the compiler assume all registers are clobbered?

If it returns an unsigned char, is the callee expected to set X to 0? If it returns a signed char, is the callee expected to set X to 0 or $FF depending on the sign bit? Is there really no such thing as an 8-bit return type, and all return values are implicitly 16-bit?

The function is responsible for emptying the parameters placed on the C-stack. Are all return values in A/X or is there any case where it must leave a return value on the stack?

  1. Argument passing.

For cdecl all arguments are passed on the C-stack.

The rightmost argument has the highest address on the C-stack.

The high byte of a 16-bit argument has an address 1 higher than its low byte.

For fastcall the rightmost argument will be placed into A (if 8-bit) or A/X (if 16-bit). The rest will use the C-stack as if the cdecl convention was used with one less argument.

Fastcall is the default convention, unless the --all-cdecl command line option is used.

The command line option --standard hides the "fastcall" and "cdecl" keywords, but "fastcall" and "cdecl" always remain available.

Is there anything missing here? Are any of these statement incorrect?

  1. Use of internal ZP temporary variables.

Some of the CRT's temporary variables are allowed for use by C functions. It is unsafe to expect any of them to be preserved after any call to another C function, but can safely be used within any single function. Should the list of safe temporaries be documented?

Are there any other important details related to calling convention or implementing assembly functions callable from C?

cc65 miscompiles w/ a static variable and -O

I have an optimized C source for LZ4 (attaching if I can get github to work). Using today's master, if line 145 says "unsigned s;" the results are correct, but if I change it to "static unsigned", the results are wrong.

This is a bug in cc65, since both gcc and clang produce correct results with and without static. The codeflow always initializes it too.

Here's the asm diff, in case it helps someone.

--- wrong.s 2016-01-25 19:52:10.000000000 +0200
+++ right.s 2016-01-25 19:52:32.000000000 +0200
@@ -50,8 +50,6 @@
    .res    2,$00
 L003C:
    .res    2,$00
-L003D:
-   .res    2,$00

 .segment   "CODE"

@@ -71,12 +69,13 @@
    jsr     pushax
    jsr     decsp2
    jmp     L0038
-L0036: ldy     #$07
+L0036: jsr     decsp2
+   ldy     #$09
    jsr     ldaxysp
    sta     regsave
    stx     regsave+1
    jsr     incax1
-   ldy     #$06
+   ldy     #$08
    jsr     staxysp
    lda     regsave
    ldx     regsave+1
@@ -91,26 +90,26 @@
    sta     L003B
    stx     L003B+1
    cpx     #$00
-   bne     L0048
+   bne     L0047
    cmp     #$0F
-L0048: jsr     booleq
-   jeq     L004A
-L0049: ldy     #$07
+L0047: jsr     booleq
+   jeq     L0049
+L0048: ldy     #$09
    jsr     ldaxysp
    sta     regsave
    stx     regsave+1
    jsr     incax1
-   ldy     #$06
+   ldy     #$08
    jsr     staxysp
    lda     regsave
    ldx     regsave+1
    ldy     #$00
    jsr     ldauidx
    ldx     #$00
-   sta     L003D
-   stx     L003D+1
-   lda     L003D
-   ldx     L003D+1
+   ldy     #$00
+   jsr     staxysp
+   ldy     #$01
+   jsr     ldaxysp
    clc
    adc     L003B
    sta     L003B
@@ -119,41 +118,41 @@
    sta     L003B+1
    tax
    lda     L003B
-   lda     L003D
-   ldx     L003D+1
+   ldy     #$01
+   jsr     ldaxysp
    cpx     #$00
-   bne     L0051
+   bne     L0050
    cmp     #$FF
-L0051: jsr     booleq
-   jne     L0049
-L004A: ldy     #$05
+L0050: jsr     booleq
+   jne     L0048
+L0049: ldy     #$07
    jsr     ldaxysp
    jsr     pushax
    lda     L003B
    ldx     L003B+1
    jsr     tosaddax
-   ldy     #$00
+   ldy     #$02
    jsr     staxysp
-   ldy     #$01
+   ldy     #$03
    jsr     ldaxysp
    jsr     pushax
-   ldy     #$05
+   ldy     #$07
    jsr     ldaxysp
    jsr     decax8
    jsr     tosugtax
-   jeq     L0054
-   ldy     #$01
+   jeq     L0053
+   ldy     #$03
    jsr     ldaxysp
    jsr     pushax
-   ldy     #$05
+   ldy     #$07
    jsr     ldaxysp
    jsr     tosneax
-   jeq     L0056
-   jmp     L0058
-L0056: ldy     #$05
+   jeq     L0055
+   jmp     L0057
+L0055: ldy     #$07
    jsr     ldaxysp
    jsr     pushax
-   ldy     #$09
+   ldy     #$0B
    jsr     ldaxysp
    jsr     pushax
    lda     L003B
@@ -161,40 +160,41 @@
    jsr     _memcpy
    lda     L003B
    ldx     L003B+1
-   ldy     #$06
+   ldy     #$08
    jsr     addeqysp
    lda     L003B
    ldx     L003B+1
-   ldy     #$04
+   ldy     #$06
    jsr     addeqysp
+   jsr     incsp2
    jmp     L0037
-L0054: ldy     #$05
+L0053: ldy     #$07
    jsr     ldaxysp
    jsr     pushax
-   ldy     #$09
+   ldy     #$0B
    jsr     ldaxysp
    jsr     pushax
-   ldy     #$05
+   ldy     #$07
    jsr     ldaxysp
    jsr     _LZ4_wildCopy
    lda     L003B
    ldx     L003B+1
-   ldy     #$06
+   ldy     #$08
    jsr     addeqysp
-   ldy     #$01
+   ldy     #$03
    jsr     ldaxysp
-   ldy     #$04
+   ldy     #$06
    jsr     staxysp
-   ldy     #$01
+   ldy     #$03
    jsr     ldaxysp
    jsr     pushax
-   ldy     #$09
+   ldy     #$0B
    jsr     ldaxysp
    jsr     _LZ4_read16
    jsr     tossubax
    sta     L003C
    stx     L003C+1
-   ldy     #$06
+   ldy     #$08
    ldx     #$00
    lda     #$02
    jsr     addeqysp
@@ -207,26 +207,26 @@
    lda     L003B
    ldx     L003B+1
    cpx     #$00
-   bne     L0076
+   bne     L0075
    cmp     #$0F
-L0076: jsr     booleq
-   jeq     L0078
-L0077: ldy     #$07
+L0075: jsr     booleq
+   jeq     L0077
+L0076: ldy     #$09
    jsr     ldaxysp
    sta     regsave
    stx     regsave+1
    jsr     incax1
-   ldy     #$06
+   ldy     #$08
    jsr     staxysp
    lda     regsave
    ldx     regsave+1
    ldy     #$00
    jsr     ldauidx
    ldx     #$00
-   sta     L003D
-   stx     L003D+1
-   lda     L003D
-   ldx     L003D+1
+   ldy     #$00
+   jsr     staxysp
+   ldy     #$01
+   jsr     ldaxysp
    clc
    adc     L003B
    sta     L003B
@@ -235,29 +235,29 @@
    sta     L003B+1
    tax
    lda     L003B
-   lda     L003D
-   ldx     L003D+1
+   ldy     #$01
+   jsr     ldaxysp
    cpx     #$00
-   bne     L007F
+   bne     L007E
    cmp     #$FF
-L007F: jsr     booleq
-   jne     L0077
-L0078: lda     #$04
+L007E: jsr     booleq
+   jne     L0076
+L0077: lda     #$04
    clc
    adc     L003B
    sta     L003B
-   bcc     L0082
+   bcc     L0081
    inc     L003B+1
-L0082: ldx     L003B+1
-   ldy     #$05
+L0081: ldx     L003B+1
+   ldy     #$07
    jsr     ldaxysp
    jsr     pushax
    lda     L003B
    ldx     L003B+1
    jsr     tosaddax
-   ldy     #$00
+   ldy     #$02
    jsr     staxysp
-   ldy     #$05
+   ldy     #$07
    jsr     ldaxysp
    jsr     pushax
    lda     L003C
@@ -268,15 +268,15 @@
    lda     L003B
    ldx     L003B+1
    cpx     #$00
-   bne     L008B
+   bne     L008A
    cmp     #$08
-L008B: jsr     boolult
+L008A: jsr     boolult
    cpx     #$00
-   bne     L008C
+   bne     L008B
    cmp     #$00
-L008C: jsr     boolne
-   jeq     L0087
-   ldy     #$05
+L008B: jsr     boolne
+   jeq     L0086
+   ldy     #$07
    jsr     ldaxysp
    jsr     pushax
    lda     L003C
@@ -285,7 +285,7 @@
    jsr     ldauidx
    ldy     #$00
    jsr     staspidx
-   ldy     #$05
+   ldy     #$07
    jsr     ldaxysp
    jsr     pushax
    lda     L003C
@@ -294,7 +294,7 @@
    jsr     ldauidx
    ldy     #$01
    jsr     staspidx
-   ldy     #$05
+   ldy     #$07
    jsr     ldaxysp
    jsr     pushax
    lda     L003C
@@ -303,7 +303,7 @@
    jsr     ldauidx
    ldy     #$02
    jsr     staspidx
-   ldy     #$05
+   ldy     #$07
    jsr     ldaxysp
    jsr     pushax
    lda     L003C
@@ -332,14 +332,14 @@
    sta     L003C+1
    tax
    lda     L003C
-   ldy     #$05
+   ldy     #$07
    jsr     ldaxysp
    jsr     incax4
    jsr     pushax
    lda     L003C
    ldx     L003C+1
    jsr     _LZ4_copy4
-   ldy     #$04
+   ldy     #$06
    ldx     #$00
    lda     #$08
    jsr     addeqysp
@@ -356,9 +356,9 @@
    jsr     ldaidx
    ldx     #$00
    cmp     #$80
-   bcc     L00A8
+   bcc     L00A7
    dex
-L00A8: eor     #$FF
+L00A7: eor     #$FF
    sec
    adc     L003C
    sta     L003C
@@ -368,14 +368,14 @@
    sta     L003C+1
    tax
    lda     L003C
-   jmp     L00A9
-L0087: ldy     #$05
+   jmp     L00A8
+L0086: ldy     #$07
    jsr     ldaxysp
    jsr     pushax
    lda     L003C
    ldx     L003C+1
    jsr     _LZ4_copy8
-   ldy     #$04
+   ldy     #$06
    ldx     #$00
    lda     #$08
    jsr     addeqysp
@@ -383,54 +383,54 @@
    clc
    adc     L003C
    sta     L003C
-   bcc     L00B1
+   bcc     L00B0
    inc     L003C+1
-L00B1: ldx     L003C+1
-L00A9: ldy     #$01
+L00B0: ldx     L003C+1
+L00A8: ldy     #$03
    jsr     ldaxysp
    jsr     pushax
-   ldy     #$05
+   ldy     #$07
    jsr     ldaxysp
    ldy     #$0C
    jsr     decaxy
    jsr     tosugtax
    cpx     #$00
-   bne     L00B6
+   bne     L00B5
    cmp     #$00
-L00B6: jsr     boolne
-   jeq     L00B2
-   ldy     #$01
+L00B5: jsr     boolne
+   jeq     L00B1
+   ldy     #$03
    jsr     ldaxysp
    jsr     pushax
-   ldy     #$05
+   ldy     #$07
    jsr     ldaxysp
    jsr     decax5
    jsr     tosugtax
-   jeq     L00B7
-   jmp     L0058
-L00B7: ldy     #$05
+   jeq     L00B6
+   jmp     L0057
+L00B6: ldy     #$07
    jsr     ldaxysp
    jsr     pushax
-   ldy     #$05
+   ldy     #$07
    jsr     ldaxysp
    jsr     decax8
    jsr     tosultax
-   jeq     L00B9
-   ldy     #$05
+   jeq     L00B8
+   ldy     #$07
    jsr     ldaxysp
    jsr     pushax
    lda     L003C
    ldx     L003C+1
    jsr     pushax
-   ldy     #$07
+   ldy     #$09
    jsr     ldaxysp
    jsr     decax8
    jsr     _LZ4_wildCopy
-   ldy     #$03
+   ldy     #$05
    jsr     ldaxysp
    jsr     decax8
    jsr     pushax
-   ldy     #$07
+   ldy     #$09
    jsr     ldaxysp
    jsr     tossubax
    clc
@@ -441,18 +441,18 @@
    sta     L003C+1
    tax
    lda     L003C
-   ldy     #$03
+   ldy     #$05
    jsr     ldaxysp
    jsr     decax8
-   ldy     #$04
+   ldy     #$06
    jsr     staxysp
-L00B9: jmp     L00C6
-L00C4: ldy     #$05
+L00B8: jmp     L00C5
+L00C3: ldy     #$07
    jsr     ldaxysp
    sta     regsave
    stx     regsave+1
    jsr     incax1
-   ldy     #$04
+   ldy     #$06
    jsr     staxysp
    lda     regsave
    ldx     regsave+1
@@ -470,27 +470,28 @@
    jsr     ldauidx
    ldy     #$00
    jsr     staspidx
-L00C6: ldy     #$05
+L00C5: ldy     #$07
    jsr     ldaxysp
    jsr     pushax
-   ldy     #$03
+   ldy     #$05
    jsr     ldaxysp
    jsr     tosultax
-   jne     L00C4
-   jmp     L00CA
-L00B2: ldy     #$05
+   jne     L00C3
+   jmp     L00C9
+L00B1: ldy     #$07
    jsr     ldaxysp
    jsr     pushax
    lda     L003C
    ldx     L003C+1
    jsr     pushax
-   ldy     #$05
+   ldy     #$07
    jsr     ldaxysp
    jsr     _LZ4_wildCopy
-L00CA: ldy     #$01
+L00C9: ldy     #$03
    jsr     ldaxysp
-   ldy     #$04
+   ldy     #$06
    jsr     staxysp
+   jsr     incsp2
 L0038: jmp     L0036
 L0037: ldy     #$07
    jsr     ldaxysp
@@ -499,7 +500,7 @@
    jsr     ldaxysp
    jsr     tossubax
    jmp     L0020
-L0058: ldx     #$FF
+L0057: ldx     #$FF
    lda     #$FF
    jmp     L0020
 L0020: ldy     #$0E

C64 randomize function using wrong timer byte

Current function looks like this:

_randomize:
ldx VIC_HLINE ; Use VIC rasterline as high byte
lda TIME ; Use 60HZ clock as low byte
jmp _srand ; Initialize generator

Second line should be using TIME+2, similar to the way the Atari version uses RTCLOK+2, since the first byte is the longest time to change at 18+ minutes. +2 gets you the jiffy counter.

Confusing errors with mixed declarations and code

void foo() {
        int a;
        a = 5;
        int b;
}

produces

foo.c(4): Error: Expression expected
foo.c(4): Warning: Statement has no effect
foo.c(4): Error: ';' expected
foo.c(4): Error: Undefined symbol: 'b'
foo.c(4): Warning: Statement has no effect

It does not claim full C99 support, but it would be nice to have a clearer warning, like gcc's "ISO C90 forbids mixed declarations and code". The current warnings make it really unclear what is the problem.

Comparing const and non-const pointers

According to C89, comparing a non-const pointer to a const pointer is valid.

The following fails with cc65 and succeeds with gcc -ansi -pedantic.

unsigned char foo(const unsigned char *cp, unsigned char *p) {
        return cp == p;
}

ca65 dependency file generation doesn't escape spaces in paths

Ca65 dependency file generation doesn't escape spaces in paths, causing ambiguity:

; test2.s ("test test.h" is an empty file)
.include "test test.h"

Command:

cl65 --create-dep test2-dep.txt test2.s

Result (test2-dep.txt):

test2.o:    test2.s test test.h

test2.s test test.h:

The C compiler does do it properly (by escaping with "", similarly to how gcc does it):

// test1.c ("test test.h" is an empty file)
#include "test test.h"

Command:

cl65 --create-dep test1-dep.txt test1.c

Result (test1-dep.txt):

test1.o:    test1.c test\ test.h

test1.c test\ test.h:

Array size compile-time optimization stops halfway

The following file errors out on line 3. It compiles fine on gcc with -ansi -pedantic, which forces it to hard c89 mode.

#define LZO_MAX(a,b)        ((a) >= (b) ? (a) : (b))
unsigned char c[2*4];
unsigned char b[2*LZO_MAX(8,sizeof(int))];

cc65 is missing useful Apple 2 Text macros

On the Apple ][ //e //c programmers need a variety of way of controlling ASCII (high-bit off), APPLE (high-bit on), and Control characters ($0..$1F).

Since out-the-box cc65 doesn't have anything useful in asminc/, here is an apple2text.inc file that provides this functionality.

; NOTE: There is no "standard" directive to set the high bit on ASCII text.
;
; * The assembler used in the redbook uses `msb on` and `msb off`
; * S-C Macro Assembler used
;     .AS for normal ASCII, and
;     .AT for normal ASCII but the last char has the high bit ON
; * Merlin uses yet another variation:
;     ASC 'Hello'   ; high bit is off
;     ASC "Hello"   ; high bit is ON
;   See the new Merlin-32 project
;   http://brutaldeluxe.fr/products/crossdevtools/merlin/
; 
; "The 'nice' thing about standards, is that there are so many to pick from!"

; Force APPLE 'text' to have high bit on
.macro AS text
    .repeat .strlen(text), I
        .byte   .strat(text, I) | $80
    .endrep
.endmacro

; Force ASCII 'text' but last character has high bit on
; Will display as FLASHING characters
; NOTE: There is no lowercase flashinng characters - they will appar as gibbersh!
.macro AT text
    .repeat .strlen(text)-1, I
        .byte   .strat(text, I) & $7F
    .endrep
    .byte   .strat(text, .strlen(text)-1) | $80
.endmacro

; Force APPLE 'text' with high bit on but last character has high bit off
.macro AR text
    .repeat .strlen(text)-1, I
        .byte   .strat(text, I) | $80
    .endrep
    .byte   .strat(text, .strlen(text)-1) & $7F
.endmacro

; Force ASCII 'text' to be control chars: $00..$1F
; Will display as INVERSE characters
.macro AC text
    .repeat .strlen(text), I
        .byte   .strat(text, I) & $1F
    .endrep
.endmacro

Example usage:

AppleTextDemo:
    AR "ABC"            ; NORMAL : APPLE high-bit text, last char is ASCII (FLASHING)
    AS "Hello world"    ; NORMAL : APPLE high-bit text
    AT "XYZ"            ; FLASH  : ASCII text, last char is APPLE high-bit (NORMAL)
    AC "DEFM"           ; INVERSE: Force to be control chars0 $0..$1F (INVERSE)

Suppress "Suspicious address expression" for 65816 direct page

On 65816, it is common to access several addresses in the same 256-byte page of bank $00 using direct page addressing mode. However, ca65 raises a "Range error" when using this with addresses not in the true zero page, and attempts to work around this error result in a "Suspicious address expression" warning. The docs mention neither this warning nor a way to suppress it.

.p816
VMDATA = $002118
.smart
.segment "CODE"
  sep #$20
  ; First set the direct page base
  pea linebuf & ~$FF
  pld
  ; Now try to add to direct page
  lda <linebuf      ; This is suspicious
  lda z:<linebuf    ; So is this
  lda z:linebuf     ; And this just makes a range error
  pea VMDATA & ~$FF
  pld
  sta <VMDATA       ; But this isn't
forever: jmp forever
.segment "BSS"      ; linker script puts this in $000200-$001FFF
linebuf: .res 32

"register" variable not always optimized

Simple source code to show the issue:

char c, r;
void main(void)
{
    register char * p;
    c = p[10];
    r = 20;
    c = p[r];
}

the statement "c = p[10]" gets compiled as:

ldy     #$0A
lda     (regbank+4),y
sta     _c

but the statement "c = p[r]" gets compiled as:

lda     regbank+4
ldx     regbank+4+1
ldy     _r
sta     ptr1
stx     ptr1+1
lda     (ptr1),y
sta     _c

where one might expect instead:

ldy     _r
lda     (regbank+4),y
sta     _c

It is compiled with this parameters:

cc65 -o ../obj/main.s -Cl -Oirs -T -t c64 main.c

ca65 line continuation feature bug

Using the line continuation feature results in ca65 skipping the leftmost column when a line is continued. Example:

.linecont +

foo = \
22

; outputs "foo:2"
.out .sprintf("foo:%d", foo)

; this does not cause an error!
bar = \
"1

; outputs "bar:1"
.out .sprintf("bar:%d", bar)

There was some discussion on how to fix this earlier. I'll submit a patch of how I prefer it, opinions welcomed.

edit:update example.

patch: #183

Possible compiler bug with pointer variable vs direct pointer

This file:

#include <nes.h>

int main() {

        unsigned char *ppuaddr = (unsigned char *) 0x2006;
        unsigned char *ppudata = (unsigned char *) 0x2007;
        unsigned char *ppuctrl = (unsigned char *) 0x2000;
        unsigned char *ppumask = (unsigned char *) 0x2001;
        unsigned char *ppuscroll = (unsigned char *) 0x2005;

        waitvblank();

        *((unsigned char*)0x2006) = 0x3F;
        *((unsigned char*)0x2006) = 0x00;
//      *((unsigned char*)0x2007) = 1;
        *ppudata = 1; // uncomment the above and comment this -> it works

        *ppuscroll = 0;
        *ppuscroll = 0;

        *ppumask = 8;

        while (1);
        return 0;
}

fails when run - the background color is grey. If I comment the *ppudata assignment and uncomment the direct pointer assignment, it works - the bg color is blue.

I don't speak enough asm to say why the pointer-var fails and direct pointer works. Here is the diff of the assemblies:

--- wrong.s     2015-10-05 22:33:49.000000000 +0300
+++ right.s     2015-10-05 22:33:43.000000000 +0300
@@ -46,13 +46,9 @@
        ldx     #$00
        lda     #$00
        sta     $2006
-       ldy     #$07
-       jsr     ldaxysp
-       jsr     pushax
        ldx     #$00
        lda     #$01
-       ldy     #$00
-       jsr     staspidx
+       sta     $2007
        ldy     #$01
        jsr     ldaxysp
        jsr     pushax

cc65 fails to warn about a function returning struct

This program gives wrong results when built on cc65, regardless of -O. The function is correct, and it succeeds when built on gcc.

(I was looking for a faster div() alternative. Still need to try a modified stdlib div() that uses unsigned division instead.)

#include <nes.h>
#include <conio.h>
#include <stdio.h>
#include <stdlib.h>
#include <stdint.h>

typedef uint16_t u16;
typedef uint8_t u8;

typedef struct {
    u16 quot;
    u16 rem;
} udiv_t;

udiv_t div3(u16 in) {

    udiv_t u;
    u16 q = 0;

    while (in >= 300) {
        in -= 300;
        q += 100;
    }

    while (in >= 30) {
        in -= 30;
        q += 10;
    }

    while (in >= 3) {
        in -= 3;
        ++q;
    }

    u.quot = q;
    u.rem = in;

    return u;
}

int main() {

    u16 i;
    div_t d;
    udiv_t u;

    for (i = 1024; i; i--) {
        d = div(i, 3);
        u = div3(i);

        if (d.quot != u.quot || d.rem != u.rem) {
            cprintf("Mismatch at %u/3, div %u %u, div3 %u %u\n", i,
                d.quot, d.rem, u.quot, u.rem);
            break;
        }
    }

    while (1);

    return 0;
}

Addition of an u8 and a func returning u8 -> wrong result

u8 rand8();

void play() {
    static u16 nextpow;
    nextpow = 180 + rand8();
    // print it on the screen
}

After this, var contains 9003, which is no way possible. rand8 returns 119 in this case. This happens both with and without -O.

If I write it in two lines, "var = 180; var += rand8();" the result is correct.

Asm with -O:

; ---------------------------------------------------------------
; void __near__ play (void)
; ---------------------------------------------------------------

.segment        "CODE"

.proc   _play: near

.segment        "BSS"

L0024:
        .res    2,$00
L0026:
        .res    5,$00

.segment        "CODE"

;
; nextpow = 180 + rand8();
;
        jsr     _rand8
        clc
        adc     #$B4
        bcc     L0029
        inx
L0029:  sta     L0024
        stx     L0024+1
;
; utoa4(scorebuf, nextpow);
;
...

Loop nesting bug, inner continue causes outer continue

I have a program of this type:

while (1) {
    // game loop
    for (i = 0; i < 40; ++i) {
        if (something)
            continue;
        if (u16 var - another u16 var > 128)
            continue;
    }
}

The second continue randomly causes a continue of the outer while loop. Everything after it doesn't happen, if I comment out the second check, everything works fine.

I have yet to succeed in reproducing it in a simpler sample, but when I do, I'll post one. It's not a stack overflow, at least according to cc65's --check-stack option.

It happens both with and without -O.

Supervision target needs some love.

Hi,

  • I just noticed that I broke the Supervision target with aaf90c1 in 2010. So nearly six years nobody tried to use the target.
  • The C library for the Supervision target doesn't contain any "real" code.

I see two options:

  • There's some momentum to fix the traget and to provide at least some minimal functionality (like CONIO).
  • I remove the target.

Two bugs in the string output functions

  1. sprintf(), snprintf(), vsprintf(), and vsnprintf() might crash if NULL (0) is used as the first (buffer pointer) argument.
  2. snprintf() and vsnprintf() always return zero when the second (buffer-length) argument is zero. They should return the same value that they do return when the buffer length isn't zero (i.e., the length of the string that the format produces).

P.S. Assign me to this one.

Internal compiler error: Code generation messed up: StackPtr is -4, should be -2

This file causes cc65 to ICE.

Code generation messed up: StackPtr is -4, should be -2

Input: if (tableType == byU32) { U32* hashTable = (U32*) tableBase; return hashTable[h] + srcBase; }
cl65: Subprocess `cc65' aborted by signal 6

# include <stdint.h>
typedef  uint8_t BYTE;
typedef uint16_t U16;
typedef uint32_t U32;
typedef  int32_t S32;

typedef enum { byPtr, byU32, byU16 } tableType_t;

static const BYTE* LZ4_getPositionOnHash(U32 h, void* tableBase, tableType_t tableType, const BYTE* srcBase)
{
    if (tableType == byPtr) { const BYTE** hashTable = (const BYTE**) tableBase; return hashTable[h]; }
    if (tableType == byU32) { U32* hashTable = (U32*) tableBase; return hashTable[h] + srcBase; }
    { U16* hashTable = (U16*) tableBase; return hashTable[h] + srcBase; }   /* default, to ensure a return */
}

Recursive macro issue in ca65

I tried modifying one of the push macros in the examples by adding a fourth argument:

.macro  push    r1, r2, r3, r4
        lda     r1
        pha
.if     .paramcount > 1
        push    r2, r3, r4
.endif
.endmacro

push 1, 2, 3, 4

While the original three-argument version worked fine, this version fails spectacularly with a ton of error messages about an illegal addressing mode. I have no idea what the problem is.

The .ifblank/.exitmacro equivalent of this macro works fine, by the way; it's only the .paramcount version that doesn't work.

Indirect array accesses not ZP-optimized

It seems accesses of the type "array[var]" do not get the one-byte (or more) reduction, even when both array and var are in ZP. The direct type, "array[13]", does get it.

All under -Oisr.

Recommend Projects

  • React photo React

    A declarative, efficient, and flexible JavaScript library for building user interfaces.

  • Vue.js photo Vue.js

    🖖 Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.

  • Typescript photo Typescript

    TypeScript is a superset of JavaScript that compiles to clean JavaScript output.

  • TensorFlow photo TensorFlow

    An Open Source Machine Learning Framework for Everyone

  • Django photo Django

    The Web framework for perfectionists with deadlines.

  • D3 photo D3

    Bring data to life with SVG, Canvas and HTML. 📊📈🎉

Recommend Topics

  • javascript

    JavaScript (JS) is a lightweight interpreted programming language with first-class functions.

  • web

    Some thing interesting about web. New door for the world.

  • server

    A server is a program made to process requests and deliver data to clients.

  • Machine learning

    Machine learning is a way of modeling and interpreting data that allows a piece of software to respond intelligently.

  • Game

    Some thing interesting about game, make everyone happy.

Recommend Org

  • Facebook photo Facebook

    We are working to build community through open source technology. NB: members must have two-factor auth.

  • Microsoft photo Microsoft

    Open source projects and samples from Microsoft.

  • Google photo Google

    Google ❤️ Open Source for everyone.

  • D3 photo D3

    Data-Driven Documents codes.