EMBEDDED GRAPHICS and FONTS in Aztec-C Programs

Started by BillBuckels, April 13, 2008, 11:07 PM

Previous topic - Next topic

0 Members and 1 Guest are viewing this topic.

BillBuckels

Adding Graphics and Fonts to a Compiled C64 Program

Use the Source Luke!

This program is compiled in Microsoft 16 bit C and is provided with the Aztec C64 cross-compiler for Windows XP/MS-DOS. It is based on a similar program for the Aztec C Apple IIe ProDOS 8 cross-compiler for Windows XP/MS-DOS. Both Cross compilers are available from the Aztec C website.


/* ADDLOGO(C) Copyright Bill Buckels 2007 */
/* All Rights Reserved. */

/* You have a royalty free right to use this program in any way
   you find useful provided that you agree that Bill Buckels
   has no Warranty Obligations or Liability in any way whatsoever. */

/* adds a HIRES C64 Bitmap and Palette to an AztecC64 Program */
/* also adds a bitmap font for HIRES mode */
/* this is a 16 bit program compiled under MSC 8,00c */
/* 16 bit utility for compatibility with compiler environment. */

/* note: this utility is designed for use with LN65 output */
/*       with base 810 and code and data offset past default screen area */
/*       Also the LN65 output file must not have an extension */

/* this utility must be run before MKBASIC and after LN65 */
/* this utility does not overwrite the input file */
/* it creates an output file with a .SYS extension */

/* MKBASIC is then run on the SYS file to create a PRG */

/* this utility has also been extended to embed multicolor images
   and cursor libraries (cursor.vhi) and to use default naming of title.*
   as a convenient method of organizing a C64 aztec.c graphics project. */

/* I have stopped short of allowing the IBM OEM 256 character font set
   to be embedded. Multilingual programs using the OEM special characters
   are therefore not possible using this scheme but I have provided
   this source and those characters in case someone gets ambitious
   and wants to take this further. */


#include <malloc.h>
#include <stdlib.h>
#include <string.h>
#include <stdio.h>
#include <io.h>
#include <fcntl.h>

char bmapbuffer[8000];
char vrambuffer[1000];

/* 1024 byte character array of IBM-PC 8 x 8 bitmapped rom font
   for ascii values 0-127 for static storage in C64 Aztec Programs.
   This will be inserted 3072 bytes below screen and will plot
   verbatim in HIRES mode. vram can be used to select colors. */
unsigned char fontbuffer[1024] = {
0,0,0,0,0,0,0,0,
126,129,165,129,189,153,129,126,
126,255,219,255,195,231,255,126,
108,254,254,254,124,56,16,0,
16,56,124,254,124,56,16,0,
56,124,56,254,254,124,56,124,
16,16,56,124,254,124,56,124,
0,0,24,60,60,24,0,0,
255,255,231,195,195,231,255,255,
0,60,102,66,66,102,60,0,
255,195,153,189,189,153,195,255,
15,7,15,125,204,204,204,120,
60,102,102,102,60,24,126,24,
63,51,63,48,48,112,240,224,
127,99,127,99,99,103,230,192,
153,90,60,231,231,60,90,153,
128,224,248,254,248,224,128,0,
2,14,62,254,62,14,2,0,
24,60,126,24,24,126,60,24,
102,102,102,102,102,0,102,0,
127,219,219,123,27,27,27,0,
62,99,56,108,108,56,204,120,
0,0,0,0,126,126,126,0,
24,60,126,24,126,60,24,255,
24,60,126,24,24,24,24,0,
24,24,24,24,126,60,24,0,
0,24,12,254,12,24,0,0,
0,48,96,254,96,48,0,0,
0,0,192,192,192,254,0,0,
0,36,102,255,102,36,0,0,
0,24,60,126,255,255,0,0,
0,255,255,126,60,24,0,0,
0,0,0,0,0,0,0,0,
48,120,120,120,48,0,48,0,
108,108,108,0,0,0,0,0,
108,108,254,108,254,108,108,0,
48,124,192,120,12,248,48,0,
0,198,204,24,48,102,198,0,
56,108,56,118,220,204,118,0,
96,96,192,0,0,0,0,0,
24,48,96,96,96,48,24,0,
96,48,24,24,24,48,96,0,
0,102,60,255,60,102,0,0,
0,48,48,252,48,48,0,0,
0,0,0,0,0,48,48,96,
0,0,0,252,0,0,0,0,
0,0,0,0,0,48,48,0,
6,12,24,48,96,192,128,0,
124,198,206,222,246,230,124,0,
48,112,48,48,48,48,252,0,
120,204,12,56,96,204,252,0,
120,204,12,56,12,204,120,0,
28,60,108,204,254,12,30,0,
252,192,248,12,12,204,120,0,
56,96,192,248,204,204,120,0,
252,204,12,24,48,48,48,0,
120,204,204,120,204,204,120,0,
120,204,204,124,12,24,112,0,
0,48,48,0,0,48,48,0,
0,48,48,0,0,48,48,96,
24,48,96,192,96,48,24,0,
0,0,252,0,0,252,0,0,
96,48,24,12,24,48,96,0,
120,204,12,24,48,0,48,0,
124,198,222,222,222,192,120,0,
48,120,204,204,252,204,204,0,
252,102,102,124,102,102,252,0,
60,102,192,192,192,102,60,0,
248,108,102,102,102,108,248,0,
126,96,96,120,96,96,126,0,
126,96,96,120,96,96,96,0,
60,102,192,192,206,102,62,0,
204,204,204,252,204,204,204,0,
120,48,48,48,48,48,120,0,
30,12,12,12,204,204,120,0,
230,102,108,120,108,102,230,0,
96,96,96,96,96,96,126,0,
198,238,254,254,214,198,198,0,
198,230,246,222,206,198,198,0,
56,108,198,198,198,108,56,0,
252,102,102,124,96,96,240,0,
120,204,204,204,220,120,28,0,
252,102,102,124,108,102,230,0,
120,204,224,112,28,204,120,0,
252,48,48,48,48,48,48,0,
204,204,204,204,204,204,252,0,
204,204,204,204,204,120,48,0,
198,198,198,214,254,238,198,0,
198,198,108,56,56,108,198,0,
204,204,204,120,48,48,120,0,
254,6,12,24,48,96,254,0,
120,96,96,96,96,96,120,0,
192,96,48,24,12,6,2,0,
120,24,24,24,24,24,120,0,
16,56,108,198,0,0,0,0,
0,0,0,0,0,0,0,255,
48,48,24,0,0,0,0,0,
0,0,120,12,124,204,118,0,
224,96,96,124,102,102,220,0,
0,0,120,204,192,204,120,0,
28,12,12,124,204,204,118,0,
0,0,120,204,252,192,120,0,
56,108,96,240,96,96,240,0,
0,0,118,204,204,124,12,248,
224,96,108,118,102,102,230,0,
48,0,112,48,48,48,120,0,
12,0,12,12,12,204,204,120,
224,96,102,108,120,108,230,0,
112,48,48,48,48,48,120,0,
0,0,204,254,254,214,198,0,
0,0,248,204,204,204,204,0,
0,0,120,204,204,204,120,0,
0,0,220,102,102,124,96,240,
0,0,118,204,204,124,12,30,
0,0,220,118,102,96,240,0,
0,0,124,192,120,12,248,0,
16,48,124,48,48,52,24,0,
0,0,204,204,204,204,118,0,
0,0,204,204,204,120,48,0,
0,0,198,214,254,254,108,0,
0,0,198,108,56,108,198,0,
0,0,204,204,204,124,12,248,
0,0,252,152,48,100,252,0,
28,48,48,224,48,48,28,0,
24,24,24,0,24,24,24,0,
224,48,48,28,48,48,224,0,
118,220,0,0,0,0,0,0,
0,16,56,108,198,198,254,0};


void stripext(char *ptr)
{
int idx, jdx = 999;

for (idx=0;ptr[idx] != 0; idx++) {
if (ptr[idx] == '.') jdx = idx;
}
if (jdx!=999)ptr[jdx]=0;

}

int blode(char *name)
{

   char fname[128];
   int fh, done1 = 0, done2 = 0;

   /* order of precedence here */
   /* if we have a cursor file we load this instead of the vram file */
   strcpy(fname,"cursor.vhi");
   fh = open(fname,O_RDONLY|O_BINARY); /* open a binary file */
   if (fh != -1) {
     read(fh,vrambuffer,2);
     read(fh,vrambuffer,1000);
     close(fh);
     done1 = 1;
   }

   /* if we don't have a cursor file we embed the vram file
      starting with the hires vram file and the multicolor
      vram file if a hires file is not found */

   /* to make this more straight forward we check for something called title.vhi */
   /* first, then title.vmc then on to the rest */

   if (done1 == 0) {
   strcpy(fname,"title.vhi");
   fh = open(fname,O_RDONLY|O_BINARY); /* open a binary file */
   if (fh != -1) {
read(fh,vrambuffer,2);
read(fh,vrambuffer,1000);
close(fh);
done1 = 1;
   }
   }
   if (done1 == 0) {
   strcpy(fname,"title.vmc");
   fh = open(fname,O_RDONLY|O_BINARY); /* open a binary file */
   if (fh != -1) {
read(fh,vrambuffer,2);
read(fh,vrambuffer,1000);
close(fh);
done1 = 1;
   }
    }
   if (done1 == 0) {
   strcpy(fname,name);
   stripext(fname);
   strcat(fname,".VHI");
   fh = open(fname,O_RDONLY|O_BINARY); /* open a binary file */
   if (fh != -1) {
read(fh,vrambuffer,2);
read(fh,vrambuffer,1000);
close(fh);
done1 = 1;
   }
   }
   if (done1 == 0) {
   strcpy(fname,name);
   stripext(fname);
   strcat(fname,".VMC");
   fh = open(fname,O_RDONLY|O_BINARY); /* open a binary file */
   if (fh != -1) {
read(fh,vrambuffer,2);
read(fh,vrambuffer,1000);
close(fh);
done1 = 1;
   }
   }


   /* we are done with the vram area now */
   /* we check for a hirea bitmap to embed as the title screen */
   /* to make this more straight forward we check for something called title.bhi */
   /* first, then title.bmc then on to the rest */

strcpy(fname,"title.bhi");
fh = open(fname,O_RDONLY|O_BINARY); /* open a binary file */
if (fh != -1) {
read(fh,bmapbuffer,2);
read(fh,bmapbuffer,8000);
close(fh);
done2 = 1;
}
    if (done2 == 0) {
   strcpy(fname,"title.bmc");
   fh = open(fname,O_RDONLY|O_BINARY); /* open a binary file */
   if (fh != -1) {
read(fh,bmapbuffer,2);
read(fh,bmapbuffer,8000);
close(fh);
done2 = 1;
   }
   }
   if (done2 == 0) {
   strcpy(fname,name);
   stripext(fname);
   strcat(fname,".BHI");
   fh = open(fname,O_RDONLY|O_BINARY); /* open a binary file */
   if (fh != -1) {
read(fh,bmapbuffer,2);
read(fh,bmapbuffer,8000);
close(fh);
done2 = 1;
   }
   }
   if (done2 == 0) {
strcpy(fname,name);
stripext(fname);
strcat(fname,".BHI");
fh = open(fname,O_RDONLY|O_BINARY); /* open a binary file */
if (fh != -1) {
read(fh,bmapbuffer,2);
read(fh,bmapbuffer,8000);
close(fh);
done2 = 1;
}
   }

   /* the programmer needs to provide some other way of
      populating colorram if the embedded title is
      a multicolor image.

      if the image has been created using frag64x the
      baggage files will be sufficient since
      the palette is identicalized and only a single
      value for each position in vram and cram are needed.

      doing it this way leaves the vstore available
      for other purposes as a static buffer. The cursor.vhi
      file is an example of that.

   */
   return 1;
}


/* action codes for inserting baggage files into program */
#define WRITE_PRG  0
#define WRITE_VRAM 1
#define WRITE_BMAP 2
#define WRITE_FONT 3

int bitcopy(char *name1, char *name2)
{
    FILE *fp,*fp2;
    unsigned char c;

    long target,count=0;
    int vcount=0,bcount=0,fcount=0, action=WRITE_PRG;

    if((fp=fopen(name1,"rb"))==NULL){
       perror(name1);
       return -1;
    }
    fp2=fopen(name2,"wb");
    target=filelength(fileno(fp));

    while(count<target)
        {
        c=fgetc(fp);
        action = WRITE_PRG;

        /* $2000 - $800 - $810 = 4080 (1024 byte storage area 2048 bytes
                                                              below screen) */
        /* font size = 1024 - 4080 to 5103 */
        /* add 4 bytes for LN65 header for insert range */
        if(count>4083 && count < 5108) action = WRITE_FONT;

        /* $2000 - $400 - $810 = 5104 (1024 byte storage area directly
                                                         below screen) */
        /* vram size = 1000 - 5104 to 6103 */
        /* add 4 bytes for LN65 header for insert range */
        if(count>5107 && count < 6108) action = WRITE_VRAM;

        /* $2000 - $810 = 6128 (base address of screen) */
        /* screen size = 8000 - 6128 to 14127 */
        /* add 4 bytes for LN65 header for insert range */
        if(count>6131 && count < 14132) action = WRITE_BMAP;

        switch(action)
        {
case WRITE_FONT: c = fontbuffer[fcount]; fcount++; break;
case WRITE_VRAM: c = vrambuffer[vcount]; vcount++; break;
case WRITE_BMAP: c = bmapbuffer[bcount]; bcount++; break;
}

        fputc(c,fp2);
        count++;
    }
    fclose(fp);
    fclose(fp2);

   return(0);
}


void main(int argc, char *argv[])
{
    char name1[128], name2[128];

    memset(bmapbuffer,0,8000);
    memset(vrambuffer,0,1000);

    if(argc==2)
    {
strcpy(name1, argv[1]);
stripext(name1);
strcpy(name2,name1);
strcat(name2,".SYS");
blode(argv[1]);
bitcopy(name1,name2);
}
    else
    {
printf("Purpose is to embed LOGO screens in AztecC64 programs.\n");
        printf("Usage is \"ADDLOGO MYC64\" (no extension).\n");
        printf("Output is MYC64.SYS with MYC64.BHI and MYC64.VHI embedded.\n");
        printf("All files in the set must use the same base name.\n");
   }
   /* deliberately exiting with zero regardless of outcome */
   exit(0);
}



MAKEFILE


# ---------------------------------------------------------
# Microsoft C 8 by Bill Buckels 2008
# ---------------------------------------------------------
PRG=addlogo
all: ..\$(PRG).exe


$(PRG).obj: $(PRG).c
      cl -c -AL -Zp1 $(PRG).c

..\$(PRG).exe: $(PRG).obj $(PRG).c MAKEFILE
     link $(PRG).obj,..\$(PRG).exe, NUL, /ST:8192 /NOE /NOD oldnames llibce graphics, NUL
     del $(PRG).obj


BillBuckels

Adding Title Screen and Graphics Cursors to a Compiled Apple IIe ProDOS 8 Program

While off-topic to some degree, the C64 utility ADDLOGO was based on a similar program for Apple IIe programs.

Linking Aztec C Programs - General

When Aztec C cross-compilers were released, the linker provided several possible output format options (as a linker should and does). On the Apple IIe a BINARY program was Bloaded in DOS 3.3 (which is BASIC-ally the equivalent of the LOAD command on the C64).

However when the C64 program is produced, it needs a little bit of basic stuck on the beginning to BRUN the darned thing (that's not quite it but it'll do for an explanation). MKBASIC is used on the C64 to patch the beginning of the finished program (but this is hidden in MAKEFILE's that I have provided).

Anyway on the Apple IIe it is somewhat the opposite. The first 4 bytes are the BLOAD-able address of a linked BINARY program. Who needs BASIC in ProDOS anyway? So what we do when making a ProDOS program is use the little utility shown below to strip the BLOAD-able header from the SYS program and then embed whatever graphics artifacts we wish.

This then became the BASIS for my utility for the C64.



/* MAKEPRO2(C) Copyright Bill Buckels 1991-2008 */
/* All Rights Reserved. */

/* You have a royalty free right to use this program in any way
   you find useful provided that you agree that Bill Buckels
   has no Warranty Obligations or Liability in any way whatsoever. */

/* makes a prodos sys program with an embedded graphic */

/* removes the 4 byte BLOAD-able header from an Aztec C Apple IIe ProDOS Program */
/* adds a HIRES 6 Color Apple IIe Bitmap to an Aztec C Apple IIe ProDOS Program */
/* also adds a Bitmap CURSOR Library of bitmap graphics image fragments etc */

/* these are created by the designer independently using tools provided */

/* this is a 16 bit program compiled under MSC 8,00c */
/* 16 bit utility for compatibility with compiler environment. */

#include <malloc.h>
#include <stdlib.h>
#include <string.h>
#include <stdio.h>
#include <io.h>
#include <fcntl.h>

int picfile=0;
int ribfile=0;
char *far screenbuffer;
int ribsize;

/* loads a lib */
int riblode(name)
char *name;
{
   int fh;
   fh = open(name,O_RDONLY|O_BINARY); /* open a binary file */
   ribsize = read(fh,(char *)&screenbuffer[0x2000],0x2000);
   close(fh);
   return ribsize;
}



int bitcopy(char *name1, char *name2)
{
    FILE *fp,*fp2;
    unsigned char c,d;

    long target,count=0;
    int piccount=0,ribcount=0;

    if((fp=fopen(name1,"rb"))==NULL){
                                     perror(name1);
                                     return -1;
                                     }
    fp2=fopen(name2,"wb");
    target=filelength(fileno(fp));

    while(count<target)
        {
        c=fgetc(fp);
        if(count>3)
        {
            if(count>0x2003 && (picfile!=0))
            {
             if(piccount<0x2000)c=screenbuffer[piccount];
             if(piccount>=0x2000 && ribfile!=0 && ribcount<ribsize)
             {
              c=screenbuffer[piccount];
              ribcount++;
             }
             piccount++;
            }
           fputc(c,fp2);

        }
        count++;
        }
    fclose(fp);
    fclose(fp2);

   return(0);
}


main(int argc, char *argv[])
{
    FILE *picture;
    char buf[66],buf2[66];

    if(argc==2 || argc ==3 || argc==4)
    {
    sprintf(buf,"%s.SYS",argv[1]);

    if(argc>2)
    {
     if((picture = fopen(argv[2],"rb"))!=NULL)
     {
        picfile++;
        fclose(picture);
        screenbuffer = _fmalloc(0x4000);
        memset(screenbuffer,0,0x4000);
        strcpy(buf2,argv[2]);
        preparepic(buf2);
      }
    }
    if(argc>3 && picfile!=0)
        ribfile = riblode(argv[3]);

    bitcopy(argv[1],buf);
    if(picfile)_ffree(screenbuffer);

    }
    else
    {
        printf(
        "Usage is [MANX C65 EXECUTABLE PRODOS SYS FILE- NO EXTENSION]\n");
        printf("Second Argument is Picture File Name\n");
        printf("Third Argument is Library File Name\n");
    }
    exit(0);
}

#define BOT 192
#define TOP 160
#define BIN 0

preparepic(char *picname)
{
   char c=0;
   int cflag=0;

   while(picname[c]!=0)
        {
            /* rudimentary check for type of screen that we are loading */
            if(picname[c]=='.')cflag=TOP;
            if(cflag==TOP)
            {
              if(picname[c]=='B'||picname[c]=='b')cflag=BOT;

             }
            if(cflag==BOT) {
  if(picname[c]=='I'||picname[c]=='i')cflag=BIN;
}
            if(picname[c]<' ')picname[c]=0;
            else c++;
        }

   piclode(picname,cflag);
}



piclode(name,type)
char *name;
int type;
{
   int fh,y,temp,packet=40,bos=type;
   char buffer[2];

   fh = open(name,O_RDONLY|O_BINARY); /* open a binary file */

   if (type == BIN) read(fh,(char *)&screenbuffer[0],0x2000);
   else {
   read(fh,buffer,2);
   for(y=0;y<bos;y++)
   {
     gethibase(y,&temp);
     read(fh,(char *)&screenbuffer[temp],packet);
     /* read each raster to the screenbuffer */
     }
   }
   close(fh);

}

/* provides base offset for hires scanlines         */
gethibase(currentline,currentbase)
int currentline;
int *currentbase;
{
  int ybase=0,z,a;

if(currentline >63)
   {
   if (currentline < 128)
       {
ybase+=0x28;
currentline-=64;
       }
   else
       {
       ybase+=0x50;
       currentline-=128;
       }
    }

    z=(currentline>>3);
    a = (z<<7)|ybase;
    *currentbase = (currentline - (z<<3))<<10 | a;
}



The following Memory Map shows how this works in practice (on an Apple IIe)

Memory arrangement in your program whether on the Apple IIe or C64 and C128 must avoid all the hardware areas and must not clobber the operating system. These programs that I wrote to embed screens use that to their advantage and when they load, just when they start to run, (or even while loading depending on initial video mode), since they load over the screen area the embedded screen title is displayed.


One of these days when I get more time I may create a drawing for the C64 that shows the equivalent but in the meantime the drawing that I created 18 years back gets the point across.