![]() |
![]() |
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.
![]() |
Mail problems |
![]() |
File path problems |
![]() |
PEEK and POKE |
![]() |
Files, printf and fgets |
![]() |
The .ORG directive |
![]() |
Interrupt handlers in C |
![]() |
Direct console I/O |
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 rejectedall 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:
Possible workarounds:
|
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 directoryc64.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:
c:\cc65 , you should use
set CC65_HOME=c:\cc65Note: 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:-) |
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.
|
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 |
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 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
A situation, where one might use a
Since sections of absolute code are a rare exception with ca65, there
is also a directive to end a section of absolute code:
So, while the way ca65 handles things is more complex than the old
You may use .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. |
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
|
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...
|
![]() |
Back to the cc65 Main Page. |