FAQ

This page contains a list of frequently asked questions. Before posting to the mailing list or mailing me, please check if your problem is discussed here.

Contents
* Mail problems
* File path problems
* PEEK and POKE
* Files, printf and fgets
* The .ORG directive
* Interrupt handlers in C
* Direct console I/O

Mail problems
Q: My mail to the mailing list doesn't seem to get through. What happens?
A: Mail to the list has to adhere to a few rules. It must not be larger than 40KB and must not contain too much quoted material. These rules are sent to every new member in the welcome mail.

Unfortunately, the mailing list manager won't notify you if these rules are violated, it just drops the mail. So if your mails don't get through, please check them. Some people never had problems, some others have throughput rates less than 50% - usually because of quoted material. Especially Outlook users seem to be prone to this behaviour. Since you get your own mails back from the list, detecting problems is easy. If you're sure that above rules aren't the problem, see below.

Q: My mail to you/the mailing list bounces. What is wrong?
A: Due to huge amounts of spam, the musoftware mail server will filter incoming mail and reject mail from some sources. Incoming mail from several countries is rejected, Korea and China being some. I'm very sorry about that, but as long as machines in these countries are responsible for a real huge load of spam, and there is absolutely no abuse management, I see no alternative. If you want to circumvent the spam block, you may use a mail forwarder in another country.

In addition to that, direct mail from dialup/dsl/cable ranges is rejected (note: mail is rejected if it comes directly from these IP ranges - mail sent via the mail server of the ISP is ok). The reason is that - especially in the U.S. - these internet connections are abused in too many cases. If the bounce says something like

	Mail from dialup/cable rejected
	
all you have to do is to use the mail server of your ISP instead of sending the mail directly to the musoftware mail server.
Q: I don't get any error messages but my mail doesn't seem to get through - at least I don't get any replies.
A: Several U.S. american ISPs do not accept mail from the musoftware mail server for reasons unknown to me. Since all my attempts to resolve the issue have failed (often because I could not reach anyone by email - see above), I've blocked incoming mail from these domains, so people will get an error message instead of waiting for a response that will never come. The following ISPs are currently known to block mail:
  • adelphia.com/adelphia.net (2004-03)
  • bellsouth.net (2004-02)
  • rr.com (2003-10)

Possible workarounds:

  1. Talk to your provider. You are the one paying money. If they don't listen to you, they will listen to no one else. Please note that it is not enough to forward me mails from from your ISP! They will have to contact me themselves and state explicitly that their block is no longer in effect.
  2. Use another ISP. Blocking customers mail and not responding to complaints is not what I would call a responsible policy.
  3. Forward your mail via some other mail system.
File path problems
Q: The compiler does not find the standard include files, but they are definitely on my disk!
Q: The linker says
    Error: Cannot open 'c64.lib':No such file or directory
    	
c64.lib exists in the lib directory.
A: The compiler and the linker must know how to find the include files and libraries. A standard path is compiled into the binaries, but this path works only for Unix machines (provided that the include files and libraries are installed in this path, which is true if you're using one of the precompiled packages).

When running the tools under Windows or DOS, you have to tell them where the support files are installed. This is done by setting one or more of the environment variables:

  • CA65_INC (points to directory with assembler include files)
  • CC65_INC (points to directory with compiler include files)
  • LD65_CFG (points to directory with linker config files)
  • LD65_LIB (points to directory with libraries)
  • LD65_OBJ (points to directory with object files)
  • CC65_HOME (points to the cc65 file tree. If you have this one, you usually won't need the others).
As an DOS/Windows example, if you have installed cc65 in c:\cc65, you should use
    set CC65_HOME=c:\cc65
    	
Note: The Windows .exe installer package will set all necessary variables for you, so Windows users are encouraged to use it instead of the .ZIP file distribution.

Unix people usually don't have a problem setting environment variables, so I don't explain the Unix syntax here:-)

PEEK and POKE
Q: I want to write programs for my <insert name of cool 6502 based machine here>, but I need something similar to the BASIC PEEK and POKE commands to do anything useful.
A: There are several possible ways to achive what you want.
The basic method is always to use addresses cast to pointers of the needed type:
    typedef unsigned char byte;
    typedef unsigned word;
    byte B;
    word W;

    *(byte*) 0xD800 = 0x12;    /* Store a byte to address $D800 */
    *(word*) 0xC000 = 0x1234;  /* Store a word to address $C000 */
    B = *(byte*) 0xD800;       /* Read a byte from address $D800 */
    W = *(word*) 0xC000;       /* Read a word from address $C000 */
For better readability, you may want to use macros:
    #define POKE(addr,val)     (*(unsigned char*) (addr) = (val))
    #define POKEW(addr,val)    (*(unsigned*) (addr) = (val))
    #define PEEK(addr)         (*(unsigned char*) (addr))
    #define PEEKW(addr)        (*(unsigned*) (addr))
Using the macros above, PEEK and POKE behave very similar to their BASIC counterparts:
    unsigned char B;
    unsigned W;

    POKE(0xD800, 0x12);        /* Store a byte to address $D800 */
    POKEW(0xC000,0x1234);      /* Store a word to address $C000 */
    B = PEEK(0xD800);          /* Read a byte from address $D800 */
    W = PEEKW(0xC000);         /* Read a word from address $C000 */
Please note, that the addresses used must not necessarily be constant (as in the examples above). However, while the code generated by the compiler for constant addresses is quite good (they get translated into a series of load and store instructions), for non constant addresses a subroutine is called, so this is a lot slower.

For most platforms, there is even a better way, provided that you're accessing standard I/O chips from your C program. The main include directory contains a header file for each platform that defines in memory structs for the I/O locations. The struct members are usually the registers of the I/O chip, and they do already have the correct type. Accessing the SID (sound interface device) on the C64 using the predefined SID struct would read like this:

    #include <c64.h>

    static void StartNoise (void)
    /* Start noise if configured */
    {
	/* Configure noise on voice three if requested */
	if (Effects & efNoise) {
	    SID.v3.freq  = 0x4000;
	    SID.v3.ad    = 0x00;
	    SID.v3.sr    = 0xA8;
	    SID.flt_freq = 0xA000;
	    SID.flt_ctrl = 0x44;
	    SID.amp      = 0x1F;
	    SID.v3.ctrl  = 0x81;
	}
    }
As you can see, using the predefined structs makes your program even more readable.
A: Newer cc65 versions do come with an include file named peekpoke.h that defines the PEEK, POKE, PEEKW and POKEW macros as shown in the answer above. You may want to look for this file and use it, if present.
Files, printf and fgets
Q: I've written a simple hello world program:
    #include <stdio.h>
    #include <stdlib.h>
    int main (void)
    {
        printf ("Hello world!\n");
        return EXIT_SUCCESS;
    }
but it does not link. I get the following error message from the linker:
    Unresolved external _write' referenced in:
      fwrite.s(72)
Q: I've tried to use standard file functions but I do get one of the following error messages from the linker:
    Unresolved external _open' referenced in:
      _fopen.s(102)
    Unresolved external _write' referenced in:
      fwrite.s(72)
    Unresolved external _read' referenced in:
      fgetc.s(58)
Q: I'm using gets or fgets but it does not seem to work.
A: File I/O is not supported on your platform. What most people don't realize is that even printf and gets are file functions, so if C file I/O is not supported, these functions won't work.

If you need the functions just to write to the screen or read from the keyboard, consider using the conio module instead. It is consistent between all supported machines and allows much better control (setting colors and similar things).

The .ORG directive
Q: I've ported an assembler program that was originally written for some other assembler. The program uses .ORG to place code at specific addresses. While the program assembles fine with ca65, the code placement does not seem to work.
A: Most other assemblers for the 6502 handle .ORG different than ca65. The reason is, that these tools usually do not use a linker. Professional development systems separate the job of the assembler and the linker to gain more flexibility and to implement features not possible with just one tool.

If you don't have a separate linker, the assembler is responsible for code translation and code placement. With such an assembler, you use the .ORG directive to place your code at a specific address.

If you have a look at the cc65 library, you will notice that it consists of several hundred(!) assembler files. Each of these files contains one or more support subroutines or pieces of data. Without a linker, placement of this code would be a nightmare, since it depends on your program, which of these files are actually used, so the runtime address for each one is different for any program.

To solve this, ca65 creates intermediate files (object files) containing relocatable code. Instead of absolute code references, complete expression trees are stored into the object files. Placement of a code section is done by the linker, and the linker will evaluate the expressions at link time, and replace it by the real addresses. With a linker, having a lot of object files with support routines is easy: Since the linker knows which object files will be part of the final program (because it looks for external symbols in the library), it can add these modules, calculate the start address for each module, evaluate the expressions stored inside the object files, and create an executable containing binary code.

So you should not use the assembler (ca65) to place your code at a specific address. This is the job of the linker. Instead create a new segment containing your code, and use a linker config file to place this segment at the address you want. Here is an example that places some graphics data at a fixed address:

    .segment    "gfx"
    .incbin     "gfx-file"
    .code
(Note that there is no .ORG!)

In the linker config use something like

    MEMORY {
    	...
       	GFX:    start = $2000, size = $1000, type = ro, file = %O, fill = yes;
    	...
    }

    SEGMENTS {
    	...
    	gfx:    load = GFX, type = ro;
    	...
    }
You define a memory area at $2000 and tell the linker to place the segment with the name "gfx" into this area.

In the given example, it would probably be a good idea to use a separate assembler module for each binary include file that contains just the lines

    .segment    "segmentname"
    .incbin     "filename"
Now the remaining question is, why is there an .ORG command and what does it do?

Most people will never use .ORG with ca65, since things will usually work as expected without it. The linker takes care for all things needed to place some piece of code at a specific address. If the linker puts your code at address $1234, it will resolve all jumps and other references, so your code will be able to run at this address. It is even able to handle different load and run addresses (this may be needed if your code is placed in a ROM but copied into RAM before it runs).

A situation, where one might use a .ORG directive is, when your code is placed at a specific address, but it should not be run at this address. A prominent example would be some code that is loaded into the 1541 CBM floppy. Having the linker relocate the code to the load address would be wrong, since the code will be copied at a specific address into the floppy RAM. So what you need is to tell the assembler to start a section of absolute code. If you do that, the assembler will still not place the code at a specific address, but since the final location of the code is already known, there is no need to make the code relocatable (references to symbols outside the absolute code range will still create relocation entries resolved by the linker). Code created with .ORG does not have internal relocation information. It may (must) be placed at a specific address by the linker, but if this address is not equal to the absolute address used in the .ORG directive, the code will usually not run at this address. Please note that even this problem may be solved using the "load" and "run" attributes of the linker, so I cannot think of a situation that cannot be solved without using .ORG.

Since sections of absolute code are a rare exception with ca65, there is also a directive to end a section of absolute code: .RELOC switches back to relocatable mode.

So, while the way ca65 handles things is more complex than the old .ORG stuff, it is also a lot more powerful.

You may use .ORG and .RES to emulate the behaviour of simpler assemblers (this may be useful for a short time when porting old code written for other assemblers), but you loose a lot if you do this, and it's not recommended. Anyway, here is the example above again using .ORG and .RES:

    .code
    .org        $1000
    ; Add code here
    .res        $2000-*
    .incbin     "gfx-file"
Please note that the example does work only if the code above is placed at $1000 by the linker. As explained above, the assembler is not responsible for code placement, so even in this case, you don't get away without using a special linker config file.
Interrupt handlers in C
Q: I've tried to use a timer interrupt to do some work in the background. Unfortunately, as soon as I try to use any not totally trivial functions in my interrupt handler the program crashes or goes weird.
A: As any non trivial 6502 program, the code generated by cc65 makes use of the zero page. Zero page locations are also used for the argument stack, and as temporary storage for most of the runtime support functions. So when writing interrupt handlers in C, it is necessary to save the CPU registers plus these zero page locations. In addition to that, the functions that manipulate the stack pointer are not reentrant, so you have to setup a separate stack for the interrupt handler.

This means that interrupt handlers cannot be done without some assembler code. For many applications, the wrapper code written in assembler is more than what has to be done in the interrupt handler itself, so I do usually suggest to think about writing the interrupt handler completely in assembler (provided it is rather short). If you don't want to do this for some reason or another, you may want to have a look at the break handler functions in libsrc/dbg/dbgsupp.s. They do contain all code that would also be needed for an interrupt handler (saving registers and zero page locations, calling a C function).

Direct console I/O
Q: I've found a bug: cprintf doesn't seem to handle a newline correctly. It places the cursor in the next line, but doesn't move it to the beginning of the line.
Q: When outputting a newline in the last screen line using one of the conio functions, the screen doesn't scroll. Instead the program behaves erratically and does sometimes crash.
A: The conio library is different from output done via printf. It was designed primarily for programs using dialogue oriented fulls screen masks. As a consequence...
  • ...CR (\r) and LF (\n) have a different meaning. The first one moves the cursor to the beginning of the line, the second one moves it down into the next line.
  • ...conio doesn't scroll the screen, when moving below the last line. In fact, allowing this to happen may spell desaster on several machines, because for performance reasons, there are no checks.

Valid HTML 4.01! Valid CSS! faq.php; last change: 22-Jun-2021
Impressum