11

I want to compile and run a Hello World program in C with the cc65 compiler for an unexpanded VIC-20 model. For printing I'm using the conio.h function cprintf, since the conio implementation requires less memory than stdio.

The program looks like this (originally suggested by Ulrich von Bassewitz here):

  #include <stdlib.h>
  #include <conio.h>

  int main (void)
  {
      cprintf ("Hello world!\r\n");
      return EXIT_SUCCESS;
  }

I compiled with cl65 -t vic20 helloworld.c -o helloworld.prg --mapfile map.txt

The mapfile shows that the compiled code fits, stack starts at 0x1A00, so there is 151 bytes left between 0x18AF and the stack:

  Name                   Start     End    Size  Align
  ----------------------------------------------------
  ZEROPAGE              000002  00001B  00001A  00001
  LOADADDR              000FFF  001000  000002  00001
  EXEHDR                001001  00100C  00000C  00001
  STARTUP               00100D  001046  00003A  00001
  ONCE                  001047  001052  00000C  00001
  CODE                  001053  001709  0006B7  00001
  RODATA                00170A  001834  00012B  00001
  DATA                  001835  001868  000034  00001
  INIT                  001869  001882  00001A  00001
  BSS                   001883  0018AF  00002D  00001

However when I run the program on the VIC20 emulator of VICE, the program terminates without printing any visible output (except for the "READY." from the OS). It turned out that the text is actually there, but it was printed with white characters on white background, although the foreground color was the default blue.

After adding a clrscr() command, cprintf is working:

  #include <VIC20>   /* this line seems to have no effect */
  #include <stdlib.h>
  #include <conio.h>

  int main (void)
  {
      clrscr();
      cprintf ("Hello world!\r\n");
      return EXIT_SUCCESS;
  }

On the C64 the printing works independently if there is a clrscr() or not.

According to the cc65 documentation, conio.h should work for the VIC20 platform.

What is the reason that cprintf plays up in the version without the clrscr()?

  • 1
    are you using the conio library file, written for the VIC20? – user3629249 Oct 3 at 23:20
  • Per the documentation for VICE, need to include the appropriate header file for the CPU being emulated. In this case, #include <VIC20.h> – user3629249 Oct 3 at 23:28
  • 1
    Update - I just found out that the output is there: it printed it white on white background. – Peter B. Oct 3 at 23:39
  • 1
    BTW, I just found out that conio does not do scrolling; are you sure you want to be using that instead of stdio? If you're looking to be a "single screen app" you'd presumably want to be clearing the screen immediately and setting cursor position before every write; if you want to be a more Unixy and "just print stuff to stdout," you'd want to use stdio instead I think. (Though I admit that stdio's printf not being able to fit into an unexpanded VIC-20 may be an issue. :-)) – Curt J. Sampson Oct 4 at 11:20
  • 1
    @PeterB. If you're interested in the limits of C on the VIC-20, you've found the right place! You don't want to know how many times I blew up with a "that memory ain't thar" error from the compiler while debugging this. :-) My (limited) experience has been that conio is smaller than stdio, but if you want to do C in a limited-memory environment like this, which is fair enough, you're really going to have to know the low-level details of how it gets assembled and linked. So you can't escape from it, but you can use C effectively there if you think of it as sort of a "super-macro" assembler. – Curt J. Sampson Oct 4 at 16:54
17

The Fix for cc65

This was discussed in cc65 issue #946 and fixed by cc65 PR #965. That was merged to the master branch on 2019-10-26, and should appear in V2.19 of cc65. (I don't know when that release will happen, however; it's been five months since the last release.)

It is, however, quite easy to do your own build of cc65 if you want to use the master branch version (which seems quite stable) for your project. For one example of how to do this see my vic20cc65 repo (which is what I used for debugging this issue and testing the fix).¹

The Workaround to Avoid the Problem

This is necessary only when using a version of cc65 without the fix above, such as v2.18 or earlier.

On the VIC-20, always call cputc('\r') or a routine that does a cursor move (e.g., gotoxy(), clrscr()) before printing anything else with conio, i.e., anything that uses cputc() to print, including cputs() and cprintf().

The Details

There is a bug in the VIC 20 Kernal PLOT routine at $FFF0 that sets the pointer into the colour RAM to the wrong value. cc65 has a workaround for this in libsrc/vic20/kplot.s. I suspect that this is properly used by cputc() and other routines, but from reading the code it's clear that cputc() would only call it when a CR is printed.

Thus, at startup the Kernal has left the colour RAM pointer pointing at the wrong location as compared to the screen RAM pointer, and cputc() sets colour values in the wrong memory locations until the working version of PLOT in kplot.s has been called.

The Debugging

asminc/vic20.inc in the cc65 source code contains definitions for locations storing the current screen position, colour and so forth on the VIC-20. All the symbolic names and locations below come from that.

libsrc/vic20/cputc.s contains the cputc() function which is used by cputs(), cprintf() and so on. This function uses and updates locations from the file above as it deposits values into the screen and colour RAM.

To track down the problem, I used the program listed below to print out various screen-related pointers and values before and after various calls.

Going through the various values used by cputc():

  • The colour setting itself, CHARCOLOR ($286) appears to be fine; it's $06 (blue) when the program starts and remains that way throughout.
  • CURS_X ($D3) and CURS_Y ($D6) seem fine on entry, pointing to 0,6 at start and 0,8 after gotoxy(0,8).
  • SCREEN_PTR also seems ok: it starts $1E84 (132 = 6*22+0 chars from the screen base) and after gotoxy(0,8) changes to $1EB0 (176 = 8*22+0 chars from screen base).
  • CRAM_PTR starts at $966E, which is only 110 = 5*22+0 chars from the colour RAM base, and after gotoxy(0,8) changes to $96B0, 176 = 8*22+0 chars from the colour RAM base.

So this is why your output routine that eventually uses cputc() is failing at program start: the current output position in the colour RAM is at line 5, whereas the current output position in the screen RAM is at line 6, so cputc() is writing the colour information to the line above the characters it put on the screen.

The Program

Here's the code I used to debug this. I make no claims to it being beautiful; it's just a quick hack that I threw together.

#include <vic20.h>
#include <stdlib.h>
#include <stdio.h>
#include <conio.h>

int main (void)
{
    /*  asminc/cvic.inc
        CURS_X      $D3  211
        CURS_Y      $D6  214
        SCREEN_PTR  $D1  209
        CRAM_PTR    $F3  243
        CHARCOLOR   $286 646
    */

    unsigned char *pl, *ph, cl, ch, dl, dh;
    pl = (unsigned char *) 0xF3;
    ph = (unsigned char *) 0xF4;

    cl = *pl; ch = *ph;
    gotoxy(0, 8);
    dl = *pl; dh = *ph;
    cprintf("HelloHelloHelloHello!\r\n");

    cprintf("cl=%02x ch=%02x\r\n", cl, ch);
    cprintf("dl=%02x dh=%02x\r\n", dl, dh);
    cprintf("pl=%02x ph=%02x\r\n", *pl, *ph);

    return EXIT_SUCCESS;
}

¹The vic20cc65 repo pulls in the cc65 source as a Git submodule, which locks it to a particular commit. That's typically what you'd want for "production" code where you want others to be using the version of cc65 you've tested, rather than possibly a more recent version of the master branch. If you always want to use just the latest code on master you'd want to clone the code yourself without using a submodule.

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service, privacy policy and cookie policy

Not the answer you're looking for? Browse other questions tagged or ask your own question.