The cc65 C Compiler – An
“Incredible Toy”
The AppleWin Emulator – An
“Incredible Toy”
The CiderPress Disk Image
Utility – An “Incredible Toy”
The Bmp2DHR Apple II Graphics
Converter – An “Incredible Toy”
The “Mixed-Up Toy” Project –
History
The “Mixed-Up Toy” Project –
The Adventure Continues
Capturing the Graphics
Screens
Preparing the Screens for the
Apple II
A Real Dilemma – Chopping the
Toys into Pieces
Killing the Sacred Cow –
Mixing Monochrome and Color DHR
Writing The Program – An
Incredible Journey
Mixed Text and Graphics Text
Routines
Bilingual (English and
French) Routines
The Main Program and Download
Link
It is now 2015. The days when the Apple II Computer enjoyed relatively wide use are long past. But ignoring the fact that there is no marketplace or real need for Apple II programs anymore, there has never been a better time to write Apple II programs; in today’s world of modern computers and advanced technology, a programmer can easily write and execute an Apple II program from the comfort of a modern computer. Because of the technology that we enjoy today, we can choose between virtual Apple II environments like emulators that use disk images, and real Apple II computers that have been retrofitted to use modern storage devices like USB sticks instead of floppy drives, or if we prefer, we can use “good old” serial cables or network cards to get our Apple II programs onto a real Apple II after writing them on our modern computers. So with resources that are now “at our fingertips” more than they have ever been, and as close as an Internet connection, it is very easy for a “retro-programmer” to put together a relatively high quality Apple II program using today’s “standard parts” (especially in the C programming language) given a reasonable understanding of the tools and techniques that are available to do so. (Why we would want to do such a thing is quite another matter, of course.)
Cross-development for the Apple II in the C programming language is not new, and was of course being done in the 1980’s… but today our options are virtually unlimited.
Cc65 is a “complete cross-development package for 6502-based systems” (including the Apple II) and runs from the comfort of a modern computer. It comes with a core of C language library functions and can be easily extended to accommodate writing programs that are Apple II “hardware-specific”, like programs that use Apple II Double Hi-Res Graphics (DHGR). But if you were expecting cc65 to provide something “complete” for doing Apple II “double-res” graphics you would be disappointed; a programmer actually needs to write some code.
The cc65 compiler is also not the only C cross-compiler for the Apple II. In fact, the previous version of the ““Mixed-Up Toy”” project was written in Aztec C65; an “obsolete” MS-DOS C language cross-compiler for the Apple II. But MS-DOS is “dead”… except in DosBOX and other MS-DOS emulators, and in older versions of Windows (XP and prior), so despite the fact that in many ways, Aztec C65 is a “more complete” C compiler for the Apple II than cc65, cc65 is quicker, produces smaller programs and uses modern syntax, and is in the end, a “better bet” because it runs on a modern computer and is actively developed.
Writing in C for the Apple II requires an entry-level combination skill-set that is roughly equivalent to that of an AppleSoft BASIC programmer combined with a general knowledge of the C programming language. Writing effectively in any language for the Apple II’s “hardware specific” features like DHGR requires at least a working knowledge of the Apple II and some understanding of 6502 assembly language (and the ability to “cut and paste” and modify and merge example code into your own programs).
The good news is that a great deal of Apple II DHGR reference material (and other programming and graphics reference material) is readily available to help you along.
AppleWin runs under Windows. It is the best Apple IIe emulator on The Planet. The latest changes to AppleWin incorporate Sheldon Simms AppleWin NTSC Fork. AppleWin Rocks!
Some Apple II enthusiasts might argue that a real Apple IIgs is a better Apple IIe emulator than the AppleWin software emulator, but that is incorrect. DHGR was developed for the NTSC display on the Apple IIe. The Apple IIgs DHGR colors are technically inaccurate when compared to the Apple IIe (to say the least). AppleWin Rocks!
CiderPress provides the features that modern Apple II retro-computing enthusiasts need to manage their disk and file archives; to open them, view their contents, and copy files between them and a modern computer, and even to transfer them to a real Apple II via modern media (like compact flash cards). It is probably important to note that Andy McFadden continues to actively develop CiderPress for the modern Apple II user. It is also probably noteworthy that CiderPress is the best Apple IIe disk image utility on The Planet.
Bmp2DHR is a command-line utility for converting modern bitmapped graphics to Apple II graphics. Bmp2DHR produces both full-screen DHGR Apple II graphics images and DHGR image fragments (“Sprites”) which can be displayed in an Apple II program. Bmp2DHR is a better Apple II Graphics converter by far than anything that was done when the Apple II saw wide use. Bmp2DHR believes it is the best DHGR graphics converter on The Planet. You can of course decide that for yourself, but for the graphics used by this project Bmp2DHR is the “glue” that holds it all together. Bmp2DHR has become much more than the simple DHGR “Sprite” converter that I started-out to write, but “at the end of the day”, preparing Apple IIe C graphics projects like the one described here is still its primary purpose. It runs on almost every modern computer and even on some old systems like MS-DOS and is a programmer’s tool, and not just some lame artist’s utility for doing slide-shows (although it can be used for that too if some artist was actually motivated to do some DHGR graphics for the Apple II). Until then you’re stuck with your own.
This project goes back to around 1990, or thereabouts. My son was around 6 years old at the time. I had taken an Apple II programming contract to produce a series of graphical Children’s Programs for use in Canadian Schools, and his class had Apple II computers still in use. I had purchased the Aztec C65 MS-DOS cross-compiler for the Apple II and was porting IBM-PC CGA graphics to HGR and testing my Aztec C65 routines, and sent my son to school with an Apple II version of “The Mixed-Up Toy” (written in Aztec C65) to share with his class-mates. He was already familiar with the IBM-PC version of this program so was able to “spread the joy” around his classroom a little and “a good time was had by all”. Of course he is long past this now, but it seems I am not, since I am still having a good time with “The Mixed-Up Toy”, which I resurrected “from scratch” in 2008 (after “resurrecting” my Aztec C65 MS-DOS cross-compiler).
And now (with my ongoing Apple II programming in cc65, and DHGR mode), even though it is now 2015, some 25 years later, my “adventure continues”.
The “Mixed-Up Toy” is a Children’s Game. Our saga begins in what may seem to some like an unlikely place for beginning an Apple II Program; on the IBM-PC, on December 8th, 1989, 12 years after the Apple II was released and 6 years after DHGR graphics were first introduced on the Apple IIe. So today in 2015, because modern computers no longer support MS-DOS, our saga begins with a program called Playroom in the DOSBox emulator. The DOSBox emulator provides a screen capture feature for MS-DOS graphics programs (CTRL-F5).
Because we want to use the 16-color EGA graphics screens from the “Mixed-Up Toy” activity in Playroom in our Apple II program, we use DOSBox to capture the 10 images that we need for our Apple II version and convert them from PNG to BMP files suitable for Bmp2DHR conversion to Apple II DHGR format. We also capture the Playroom title screen, and a “Mixed-Up Toy” layout screen which we need to determine how to split the toy into the “3-body parts” for the game (more about this later).
The 16-color MS-DOS EGA screen captures cannot be converted directly to the Apple IIe DHGR display. To begin with the EGA display resolution used in Playroom is 320 x 200, but the color DHGR display resolution is only effectively 140 x 192. We have no choice but to reformat these images at the risk of losing considerable detail. Fortunately despite lost pixels, DHGR’s NTSC display will help us by providing composite artifacting and the perception of additional colors, and fortunately there is enough difference between the palettes of the two displays that sufficient color error can be produced during the conversion process through error-diffusion dithering to provide the “in-between” colors to create the “deception” of somewhat equivalent graphics!
Bmp2DHR provides a dithering algorithm that capitalizes on something that Apple Computer did for the Macintosh monochrome display called Atkinson Dithering (which is modified Floyd-Steinberg Dithering using a reduced color bleed and a different weight pattern with 3 lines of error instead of 2). Bmp2DHR took Atkinson’s pattern and re-weighted it using the full color bleed of Floyd-Steinberg, and created something called Buckels Dithering which is smoother and better for Apple IIe graphics modes than both of these “Classic” dithering algorithms, so that is what we use for DHGR rendering.
When we are preparing photos or other continuous tone graphics from today’s square-pixel display, or from displays like 320 x 200 with a different pixel aspect ratio than DHGR’s rectangular pixels, we usually don’t scale by even factors and instead resample the screen with completely different colors than the original image before mapping to DHGR’s fixed palette of 15 colors. But for this project because we are scaling by even factors, we can use Bmp2DHR “horizontal pixel merging” to combines two pixels into one using the mean average RGB values of those two pixels. I should note that the 16 color EGA screens that we are using for input files are not dithered or “noisy”; they are plain solid and saturated EGA colors (and quite good quality pixel graphics). This consistency results in a relatively smooth dithering effect and merging two pixels into one beforehand ensures that color information is not lost.
So what we end-up with is 10 master images for our “Mixed-Up Toys” that are better suited for the Apple IIe’s DHGR mode than anything else we could have done (see three below):
Of all the evils, and despite the fact that often we usually see error diffusion dithering applied to continuous toned images, and positional (ordered) dithering applied to pixel graphics (usually with awful results), Bmp2DHR’s conversion with Sheldon Simms DHGR NTSC conversion palette adapted with code from Joel Yliluoma produces pretty fair results, especially when applied with the smooth pattern of the Buckels Dither. Very little real thought went into Bmp2DHR itself, because today this sort of thing seems really trivial, while at the same time surprisingly better than what we ever saw when the Apple II was in wide use. But maybe not so surprising; back then it was about making money by selling new computers and not squeezing more out of old ones.
At this point it seems we need yet another “Incredible Toy” to cut each of our 10 master images into the 3 body parts used by the game. Fortunately Bmp2DHR ships with a second utility called A2FCBmp to convert Apple II DHGR files back to Bmp files. A2FCBmp provides an option to chop an image fragment from a full-screen DHGR file and save it to Bmp using the same palette as Bmp2DHR, so we get busy and chop our converted images into pieces, then use Bmp2DHR and direct-pixel mapping (the default) to save these into Apple II DHGR image fragments.
I should probably note that direct pixel mapping is a very strong feature of Bmp2DHR. This utility is not just a dithering engine for doing error-diffusion “parlour tricks” (although it does those too). Producing graphics for an Apple II computer program is not hard but like any graphics program you don’t just sit there and expect things to turn-out without making an effort. Otherwise you might as well just play with a Paint Program and call yourself an artist and make images for slide-shows (more a fitting activity for a child although I enjoy it J). A graphics programmer is also an artist, but that skill-set is only one of many that he or she must exercise. These graphics must be polished, and pixels counted, and palettes matched. Multiple transformations are needed. Direct pixel mapping is the end-stage and is done to prepare the final graphics artifacts in this type of program, using specialized tools like Bmp2DHR and A2FCBmp.
The resulting DHGR image-fragments represent a relatively significant disk space saving over a DHGR full-screen image, because instead of 140 pixels wide, they are only 98 pixels wide and contain only visible DHGR data. We are careful to center these on the DHGR screen because we want our game to be centered rather than using the layout from the original game. We also want to provide the option of saving “Mixed-Up Toys” to disk so we can play them back in Apple II Slide-Shows and we don’t want anything else but our toys on the DHGR screen; there is really no need to display navigational artifacts from the original game for what we have in mind, since we will provide a mixed-screen menu (mixed text and graphics) that can be toggled on and off. So the original now-unwanted “caka” from the IBM-PC version is removed from the Apple II images as part of the production process.
A2FCBmp used in conjunction with Bmp2DHR produced the title screen (shown at the very beginning of this rant) which presents the credits for the artwork and original program in 560 x 192 monochrome resolution without losing detail, along with a 15 color dithered image of a representative “Mixed-Up Toy” in140 x 192 resolution. This title screen is the equivalent image from the DHGR Aztec C65 version of this project, and since the lettering is really based on HGR’s 280 x 192 monochrome resolution, each black or white pixel is doubled when the same lettering is converted to DHGR. This doubling also helps the “readability” in DHGR mode because artifacting is no worse than on the HGR equivalent.
Monochrome and color can be combined with acceptable results in both HGR and DHGR on the same Apple IIe NTSC display if it’s done carefully, although some composite artifacting will usually occur. Obviously image content plays a large part in how well this will work, but for black lettering on a white background on a simple illustration or simple converted pixel graphics image the results can be quite good. Below is a general example of the pre-processing required, and as you can see some skill with a paint-program is needed:
Derivative Image – Charlie Hebdo’s Last Cartoon |
Final Converted Image |
The final converted
image to the left is how the Apple IIe NTSC DHGR
color display views the monochrome bit pattern shown below. For this sample I
only did a “quick and dirty” edit to separate what I wanted in color from
what I wanted in black and white. Finer details like Charlie Hebdo’s signature could have also benefitted from a
monochrome equivalent instead of a colored blur. The level of detail in DHGR is really pretty amazing if a person wants to take the time to learn a thing or two about DHGR then do a careful and patient edit before a final conversion. This is left as an exercise for the reader. |
Prepare Color Elements and Convert to DHGR |
Prepare Monochrome Elements and Convert to DHGR |
Convert Back to Monochrome BMP and Combine
|
This technique can also be applied to more complex continuous tone images:
Providing monochrome banding over sections of a continuous tone DHGR image quadruples the detail but removes the color, so some sense of aesthetics must be exercised.
If what I have said up to now and throughout this rant is contrary to some common belief system or another surrounding the Apple II, I can only suggest that my results speak for themselves.
History is a matter of perspective. In the history of the computing industry the fact is that in order to move the industry forward, business decisions were made about which technology to capitalize to sell product. Technology was often under-utilized, proprietary and withheld or never advanced to its full potential due to vanishing markets or lack of return on investment. Scoping technology to meet the 3 constraints of TIME, COST, and QUALITY has generally resulted in compromised potential of some sort. But the “retro-programmer” has no constraints, therefore I expect everything that I do to be better than what everyone else has ever done, if for no other reason than the fact that I have their work as well as my own and modern tools to help me along, and no deadlines and no scope to hold me back.
One such case that I recently prepared that illustrates this notion was my Portable HackBytes project. I can hardly blame Apple Computer for developing the Desktop Publishing Industry and focusing on the Macintosh and the Apple IIgs when it came to selling computers. All the other computer companies have also done the same thing all along. But no amount of sales hype that finds its way into technical descriptions can change the fact that the “retro-programmer” has no constraints and therefore should expect everything that he or she does to be better than what everyone else has ever done. Another case that I recently prepared that illustrates this notion is Bmp2DHR itself. My work with dithering is also a good illustration of this notion.
The Apple II DHGR reference material is not a “Sacred Cow”. It was written in 1983 and revised in 1988. It is now 2015 so “therefore, some of the things originally said may seem a little stranger today, 32 years later” (unquote). Although all of my work in DHGR is based on this single document I’ve had the privilege of seeing further and so have incorporated my own strange perspective into making DHGR work for me in today’s “modern” world. I can only recommend that you adopt the same perspective in your own efforts and in reading about mine.
Sacred Quote - Apple II DHGR reference |
Heresy - My Opinion |
||
Also, the IIc and IIGS are functionally equivalent (for the purposes of this article) to a Revision B IIe with the properly-jumpered 80-column card installed, and most of the references made to the Apple IIe apply equally to the IIc and IIGS. For the purposes of
my own work, this statement is almost the equivalent of saying that the
IBM-PC 16 color EGA mode is equivalent to the Apple IIe
NTSC DHGR mode, which it is not. “I awoke one night
in Quito, Ecuador, this year and came up with a way to save a chip or two
from the Apple II, and a trivial way to have the 2 grays of the Apple II be
different (light gray and dark gray) but it’s 38 years too late. It did give
me a good smile, since I know how hard it is to improve on that
design." - Steve Wozniak, 2014. I can only conclude
from Woz’s statement that the Apple IIe and DHGR have only 15 colors. |
The Apple IIgs DHGR display is not functionally equivalent to the Apple IIe NTSC DHGR display. The Apple IIgs only emulates the Apple IIe graphics modes. The colors are wrong on the Apple IIgs. Additionally, the Apple II RGB display cannot reproduce composite artifacts. The AppleWin Apple IIe software emulator with NTSC screen rendering is far more equivalent to the Apple IIe graphics modes than a real Apple IIgs or than an Apple II with an RGB display. The Apple IIgs also introduced two grey levels to DHGR so instead of the 15 Apple IIe DHGR NTSC colors the Apple IIgs has 16 RGB colors.
|
||
On a monochrome monitor, double hi-res displays 560 horizontal by 192 vertical pixels, while on a color monitor, it allows the use of 16 colors. |
Apple IIe NTSC DHGR displays 560 x 192 black and white pixels (monochrome) and 140 x 192 color pixels. In addition to black and white there only 13 fixed DHGR colors. |
||
On a color monitor, the standard hi-res mode displays up to 140 columns of colors, each color being selected from the group of six colors available, with certain limitations. |
Apple IIe NTSC HGR displays 280 x 192 black and white pixels (monochrome) and 140 x 192 color pixels. In addition to black and white there only 4 fixed HGR colors. Only 2 fixed HGR colors may be used in a 7- monochrome pixel group (aligned on byte boundaries or “columns”). These are fixed to one of two 2-color sub-palettes of either Green-Violet or Orange-Blue.
|
||
If you have a video monitor, please use it--instead of a television set |
Throughout the 1970s, Apple did not manufacture or sell displays of any kind, instead recommending users plug-into their television sets. By early 1985 came the first color CRT's, starting with the Monitor 100, a digital RGB display for the Apple IIe, with appropriate card, followed shortly by the 14" ColorMonitor IIe, later renamed to AppleColor Composite Monitor IIe. The Apple II was introduced in 1977 with HGR graphics. DHGR followed shortly after the Apple IIe was introduced in 1983. The NTSC color display starting with the color television defines the correct Apple IIe colors. |
||
Double hi-res displays 140 columns of color, for which all 16 of the low-resolution colors are available. Table 2-The Sixteen Colors From my view this
document of Apple Computer’s is written from a hardware perspective and not a
C programmer’s perspective. There is no need to have 2 color orders for the
same colors on the same computer regardless of how the bits are arranged in
memory. The Double Lo-Res
color order differs between the Therefore the
correct “specifier” for each of the 15 colors is
the Lo-Res color number regardless of video mode. The second grey is
redundant. I also believe that this document has created the ongoing use and persistence of a second and unnecessary DHGR color order by some Apple II developers when there should only be one. |
The PATTERNS image above was distributed with BEAGLE Graphics. It is shown with the 15 DHGR NTSC colors used by both Sheldon Simms and I to convert to DHGR. Sheldon developed this palette and also developed AppleWin NTSC. BEAGLE Graphics used the low-res color order to arrange this image. All of my utilities and everything else I have ever done with DHGR and Lo-Res and Double Lo-Res uses the low-res color order and not the color order in the bitmap diagrams in Apple Computer’s document. |
Although the “Mixed-Up Toy” represents an Incredible Journey over almost 40 years of time and space, it is actually an incredibly trivial program and revolves around one very simple routine written in the C programming language that has one sole purpose; it loads DHGR image fragments to the Apple II display:
int dfraglode(char *name,int
xoff,int y1)
{
FILE *fp;
int
c,y,status=-1,width,height,packet;
unsigned dest;
fp
= fopen(name,"rb");
if (fp == NULL) return status;
for (;;) {
/* read 5 byte header */
c = fread(lodebuf,1,5,fp);
if (c != 5)
break;
if (lodebuf[0] != 'D' || lodebuf[1]
!= 'H' || lodebuf[2] != 'R') break;
width = (int)lodebuf[3];
height = (int)lodebuf[4];
/* must be in
a valid range */
if (width <
4 || width > 80) break;
if (height<
1 || height > 192) break;
/* set status
to 0 - SUCCESS */
status = 0;
packet = width
/ 2;
xoff
= xoff / 2;
for (y = 0; y
< height;y++,y1++) {
c = fread(lodebuf,1,width,fp);
if (c!=
width) {
status
= -2;
break;
}
dest = HB[y1] + xoff;
/* move to
auxiliary screen memory */
dhraux[0] = 0; /* select auxiliary memory */
memcpy((char *)dest,(char *)&lodebuf[0],packet);
/* move to
main screen memory */
dhrmain[0] = 0; /* reset to main memory */
memcpy((char *)dest,(char *)&lodebuf[packet],packet);
}
break;
}
fclose(fp);
return status;
}
All that the above code does is open a DHGR image fragment created by Bmp2DHR and read each scan-line to Apple II DHGR screen memory at the specified x and y co-ordinates. A variation of this same image fragment loader is also used in 2 other demo programs; Dhishow – a DHGR Slideshow Program in cc65, and Dhrbounce – a DHGR Animated Embedded Image Fragment Demo in cc65. The following link can also be reviewed for more information:
http://www.appleoldies.ca/bmp2dhr/sprites/
The code below calls the loader each time a new “body part” is selected; “Top”, “Middle”, or “Bottom” and also updates the text area of the 80 column Mixed Text and Graphics DHGR display.
/*
display the image fragment */
int show_silly(int sidx)
{
int
c,idx, y;
char fname[20];
if (tidx > 9)tidx = 0;
if (midx > 9)midx = 0;
if (bidx > 9)bidx = 0;
switch(sidx)
{
case 0:
y = 0;
strcpy(fname,(char
*)&enames[tidx][0]);
strcat(fname,"1.DHR");
todx = tidx;
tidx++;
break;
case 1:
y = 62;
strcpy(fname,(char
*)&enames[midx][0]);
strcat(fname,"2.DHR");
modx = midx;
midx++;
break;
case 2:
y = 132;
strcpy(fname,(char
*)&enames[bidx][0]);
strcat(fname,"3.DHR");
bodx = bidx;
bidx++;
break;
default:
return -3;
}
c = dfraglode(fname,12,y);
if(c== 0) {
/* the new name will display if in mixed
screen mode
otherwise
they can press the spacebar to toggle
between fullscreen and mixed screen and they will
see it then
*/
for (idx = 0; idx < 40; idx++)sillyname[idx] = 32;
sillyname[40] = 0;
dloprint_bottom(sillyname,3,0);
strcpy(sillyname,(char *)&sillies[todx][0]);
strcpy(proname,(char *)&enames[todx][0]);
proname[5]=0;
strcat(sillyname,".");
strcat(sillyname,(char *)&sillies[modx][0]);
strcat(proname,(char *)&enames[modx][0]);
proname[10]=0;
strcat(sillyname,".");
strcat(sillyname,(char *)&sillies[bodx][0]);
strcat(proname,(char *)&enames[bodx][0]);
proname[15]=0;
dloprint_bottom(sillyname,3,0);
}
return c;
}
The above two routines provide most of the functionality of the program, but an additional feature is to save DHGR screens of “Mixed-Up Toys” in Apple II DHGR A2FC format. The following code does this.
int a2fc_save(char *name)
{
FILE
*fp;
int
c, idx, status=0;
unsigned src;
/* remove file if
it already exists */
fp
= fopen(name,"rb");
if (fp != NULL) {
fclose(fp);
remove(name);
}
/* set filetype to binary */
_filetype = 0x06;
/* set auxiliary
type to DHGR screen address */
_auxtype =
0x2000;
fp
= fopen(name,"wb");
if (fp == NULL)return -1;
/* write 8192
bytes of auxiliary memory */
src
= 0x2000; /* set source to DHGR screen address */
for (idx = 0;idx < 8; idx++) {
/* read
auxiliary memory to main memory */
dhraux[0] = 0; /* select auxiliary memory
*/
memcpy((char *)&lodebuf[0],(char
*)src,1024);
dhrmain[0] = 0; /* reset to main memory */
c = fwrite(lodebuf,1,1024,fp);
if (c != 1024) {
status = -2;
break;
}
src
+= 1024;
}
if (status == 0) {
/* write 8192
bytes of main memory */
src
= 0x2000; /* set source to DHGR screen address */
for (idx = 0;idx < 8; idx++) {
memcpy((char *)&lodebuf[0],(char
*)src,1024);
c = fwrite(lodebuf,1,1024,fp);
if (c !=
1024) {
status =
-2; break;
}
src += 1024;
}
}
fclose(fp);
if (status != 0)
remove(name);
return status;
}
In version 2 of this program, I added each toy to a slide-show script called a “PICLIST”. For the DHGR rewrite, I haven’t bothered, so the save routine is a little simpler than the previous version. In retrospect it seems rather pointless to save these at all since the user who is presumably a child probably couldn’t care less about saving these. I have left the save feature in the program anyway more for the programmer to see how this is done. See the code below for the “wrapper” for the code above.
/*
save as an Apple II A2FC */
int save_silly()
{
int
c, oldbot = bottom;
dloclear_bottom();
if (oldbot != 1)mixedtexton();
bottom = 1;
dloprint_bottom(putdisk,0,0);
dloprint_bottom(saveprompt,1,0);
dloprint_bottom(savenot,2,0);
dloprint_bottom(sillyname,3,0);
c = cgetc();
if (c == 13 || c
== 32) {
dloclear_bottom();
dloprint_bottom(wait,0,0);
dloprint_bottom(sillyname,3,0);
c = a2fc_save(proname);
switch(c) {
case -1: dloprint_bottom(openerr,0,0); break;
case -2: dloprint_bottom(writerr,0,0);break;
default: dloprint_bottom(saveok,0,0);
}
dloprint_bottom(putprog,1,0);
dloprint_bottom(anykey,2,0);
cgetc();
}
else c = 0;
if (oldbot != 1)mixedtextoff();
bottom = oldbot;
dloclear_bottom();
dloprint_bottom(menu1,0,0);
dloprint_bottom(menu2,1,0);
dloprint_bottom(sillyname,3,0);
return c;
}
Lots of supporting routines are called in the code.
/*
the following soft switches select between upper and lower banks of video
memory */
#define dhrmain
((unsigned char*)0xC054)
#define dhraux ((unsigned char*)0xC055)
/*
80 column mode must be set before calling */
#pragma optimize (push,off)
void dhireson(void)
{
/* page 1 double
hires */
asm("sta $c050"); /* GRAPHICS
*/
asm("sta $c052"); /* GRAPHICS ONLY, NOT MIXED */
asm("sta $c054"); /* PAGE ONE */
asm("sta $c057"); /* HI-RES
*/
asm("sta $c05e"); /* TURN ON DOUBLE RES
*/
}
#pragma optimize (pop)
#pragma optimize (push,off)
void dhiresclear(unsigned
char clearcolor)
{
/* clear auxiliary
screen memory */
asm("sta $c055"); /* AUX MEM */
memset((char *)0x2000,clearcolor,8192);
/* clear main
screen memory */
asm("sta $c054"); /* MAIN MEM */
memset((char *)0x2000,clearcolor,8192);
}
#pragma optimize (pop)
/*
the following two routines work with all Apple IIe
graphics modes */
#pragma optimize (push,off)
void mixedtexton(void)
{
asm("sta $c053"); /* MIXED TEXT/GRAPHICS */
}
#pragma optimize (pop)
#pragma optimize (push,off)
void mixedtextoff(void)
{
asm("sta $c052"); /*
GRAPHICS ONLY, NOT MIXED */
}
#pragma optimize (pop)
/*
80 column mode must be set to on after calling */
#pragma optimize (push,off)
void dhiresoff(void)
{
asm("sta $c051"); /* TEXT - HIDE GRAPHICS */
asm("sta $c05f"); /* TURN OFF DOUBLE RES
*/
asm("sta $c054"); /* PAGE ONE
*/
}
#pragma optimize (pop)
The cc65 compiler does not come with Double-Res Graphics routines in its Apple II link libraries. But you don’t need them as shown above. You simply “roll your own”. For this program there really isn’t any need for cc65’s text output routines either. The whole idea behind using a C compiler to write code for the Apple II is simply to write in a widely used structured computer language. Neither AppleSoft BASIC nor 6502 Assembly language meets this requirement. At the same time, the cc65 compiler (and also the Aztec C65 C compiler) provides the ability to use assembly language where needed both inline or in separate modules. But since C accesses the Apple II’s “soft-switches” and hardware directly and compiles to 6502 assembly language, there is generally little or no advantage to using assembly language over a cc65 C program for performance or program size providing that C library calls are kept to a minimum.
The menu for this program is toggled on and off with a press of the space bar. But whether the menu is visible or not it is always updated on the bottom 4 lines of text screen memory. A simple table is referenced by the text routines for the menu.
/*
base addresses for last 4 lines of text screen memory page 1 */
unsigned dlotextbase[4]={
0x0650,
0x06D0,
0x0750,
0x07D0};
/*
some functions to output text directly to the screen in mixed mode double
lo-res and double hi-res graphics */
/*
clear the bottom 4 lines of the text screen in mixed mode double res */
void dloclear_bottom(void)
{
char *crt;
int
row, col;
char c = 32 + 128;
for (row = 0; row
< 4; row++) {
crt
= (char *)(dlotextbase[row]);
for (col = 0; col < 40; col++) {
dhraux[0] = 0; /* select auxiliary memory
*/
crt[col] = c;
dhrmain[0] = 0; /* reset to main memory */
crt[col] = c;
}
}
}
/*
print string directly to text screen memory in mixed mode double res */
/*
row settings = 0,0 to 3,79 in mixed text and graphics
mode */
void dloprint_bottom(char
*str,int row,int col)
{
char *crt;
char c;
int
x, aux = 1, idx, jdx = 0;
x = col / 2;
if (col % 2) aux = 0;
crt
= (char *)(dlotextbase[row]+x);
idx
= 0;
for (;;) {
c = str[idx];
idx++;
if (c ==
0)break;
c+=128;
if (aux == 1) {
dhraux[0] = 0; /* select auxiliary memory
*/
crt[jdx] = c;
aux = 0;
}
else {
dhrmain[0] = 0; /* reset to main memory */
crt[jdx] = c;
jdx++;
aux = 1;
}
}
/* safety play */
dhrmain[0] = 0; /* reset to main memory */
}
This program is bilingual in its own simple way, but then it is a simple program. During game play, the language can be toggled between English and French. The text strings for each language are stored as a bunch of pointers, but the display routines use pointers to these pointers based on the active language (English or French). When the language is toggled to English, the pointers to the English text are assigned and when the language is toggled to French the pointers to the French text are assigned. This is just a dead-dumb simple way of setting-up a bilingual program.
void init_strings()
{
int
idx;
for (idx = 0; idx < 10; idx++) {
if (language
== FRENCH) sillies[idx] = fnames[idx];
else sillies[idx] = enames[idx];
}
if (language ==
FRENCH) {
putdisk
= fputdisk;
saveprompt
= fsaveprompt;
savenot
= fsavenot;
wait = fwait;
openerr
= fopenerr;
writerr
= fwriterr;
saveok
= fsaveok;
putprog
= fputprog;
anykey
= fanykey;
menu1 = fmenu1;
menu2 = fmenu2;
}
else {
putdisk
= eputdisk;
saveprompt
= esaveprompt;
savenot
= esavenot;
wait = ewait;
openerr
= eopenerr;
writerr
= ewriterr;
saveok
= esaveok;
putprog
= eputprog;
anykey
= eanykey;
menu1 = emenu1;
menu2 = emenu2;
}
}
Since this is really only an overview, and you may want more, you can download the complete program at the following link for all the gory details:
http://www.appleoldies.ca/bmp2dhr/sillypreview.zip
As you can see, the main program is nothing more than a keyboard loop. How much simpler can this get?
int main(void)
{
int
c=0,cnt=0;
/* first get the language */
/* and setup the strings for either english or french
*/
language = get_language();
init_strings();
/* initialize 80 column card */
videomode(VIDEOMODE_80COL);
clrscr();
/* turn-on double hi-res and clear the dhgr screen */
dhireson();
dhiresclear(0x7f); /* clear to white */
/* load the title screen if found */
if (dfraglode("SILLY.DHR",0,0) == 0) {
c = toupper(cgetc());
/* don't allow
language to change at the title screen */
}
/* clear the keyboard buffer */
while (kbhit())cgetc();
while (c !=27) {
/* set-up
initial screen */
dloclear_bottom();
dhiresclear(0x7f); /* clear to white */
/* if some of the sillies are missing
just use the sillies that are available */
/* if all of them are missing from top,
middle, or bottom then fail quietly */
for (cnt = 0; cnt < 10; cnt++) {
if (show_silly(0) == 0) break;
}
if (cnt > 9) break;
for (cnt = 0; cnt < 10; cnt++) {
if (show_silly(1) == 0) break;
}
if (cnt > 9) break;
for (cnt = 0; cnt < 10; cnt++) {
if (show_silly(2) == 0) break;
}
if (cnt > 9) break;
dloprint_bottom(menu1,0,0);
dloprint_bottom(menu2,1,0);
/* save line 2
for additional messages etc. */
bottom = 1;
mixedtexton(); /* start in mixed screen */
for (;;) {
c = toupper(cgetc());
if (c ==
27)break;
/* remap
keys */
switch(c)
{
/* left and right arrow */
/* letter M */
case
77:
case 21:
case
8:
c =50; break; /* both remap to 2 */
/* uparrow
remaps to 1 */
/*
letter T */
case
84:
case
11: c = 49; break;
/* down arrow */
/* letter B */
case
66:
case
10: c = 51; break;
default:
break;
}
/* menu commands */
if (c ==
49 || c == 50 || c == 51) {
for (cnt = 0; cnt < 10; cnt++) {
if
(show_silly(c-49) == 0) break;
}
/* if
they have pulled the program disk out of the drive then fail */
if (cnt > 9) break;
}
if (c == 13 || c
== 32) {
if
(bottom == 0) {
mixedtexton();
bottom
= 1;
}
else {
mixedtextoff();
bottom
= 0;
}
}
if (c ==
'S' || c == 'C')save_silly();
if (c ==
'F' || c == 'A' || c == 'E') flip_language(c);
}
break;
}
/* clear double hi-res screen */
/* then turn-off double hi-res */
dhiresclear(0);
dhiresoff();
/* set to 80
column again so cc65 can exit cleanly */
videomode(VIDEOMODE_80COL);
clrscr();
while (kbhit())cgetc();
return 0;
}
I “harped-on” earlier about combining cc65’s library routines with “roll your own” routines to achieve a balance between performance and maintaining program structure (and therefore readability). The Main Program shown above is an example of that. All that I have done here is take cc65 “out of the box” as provided by Oliver Schmidt and added a handful of simple code that is complimentary to what Oliver has done with cc65.
This is an entirely different approach to how a “Classic Apple II Programmer” who follows the traditional methodology that was offered by Apple Computer “back in the day” might have approached this project. When the Apple II enjoyed wide use, technology was made available selectively if it was made available at all. Much blood, sweat, and tears followed. However, all of this including the image conversion was developed (in theory at least) before Apple Computer discontinued production of the Apple IIe, and all this could have been done back then just as easily if all of that “repressed” technology had been brought together. Today we have the advantage of “Standard Parts”. So why would you even consider doing an application the same way that old hardware guys would do considering the historically dismal results that did not capitalize on “Standard Parts”. Frankly, there is no good case today for spaghetti code or a monolithic “hardcore” assembly language application, especially when a C compiler of the quality of cc65 is available for most modern computers, and especially considering the other tools that are available as well. Unless, of course, blood, sweat and tears, and assembly language is fun for you.
This approach is not new either. As I said initially, cross-development for the Apple II and other old computers has been around “forever”. Developers like me who ported programs to other platforms did not usually attempt to write in a platform independent manner beyond using control structures and the language features of C. But the references that we used were only used to “extract” routines and to translate these in a “C-like” manner and we interfaced directly with hardware moving between C and assembler freely. Having said this, and even though this is not the way of virtual languages like Java that depend heavily on standard libraries, guys who port code in C to the Apple IIe and other micros of this vintage will be most successful by “assimilation”; by developing their own “standard parts” in conjunction with what was done before, instead of duplicating verbatim what was done before.
The source code can be reviewed for additional information about building this project. However it is probably important to note that this project is not your “run of the mill” cc65 program because it uses some customized parts, primarily to avoid the DHGR screen memory. This is all taken care-of in the MAKEFILE that is provided for this project and in the linker configuration file that is also provided.
The program comes in two parts: the program itself is the form of a ProDOS $06 binary file (called “SILLY”) which loads at $4000. DHGR Page One screen memory is at $2000 so the program runs above the DHGR screen. A ProDOS $FF sys file loader (called “SILLY.SYSTEM”) of 397 bytes is also provided. This is Oliver Schmidt’s loader.system that comes with cc65. The binary file (called “SILLY”) will also BRUN from BASIC.SYSTEM but this is not required; only ProDOS and SILLY.SYSTEM are required to run this program.
All graphics images are external and at least 2 sets of “Mixed-Up Toys” must be present for the program to work properly. They will not all fit on a floppy so the program is distributed with a bootable HDV disk image which runs the program with all the images, and a bootable 140K DSK disk image which runs the program without a title screen and with only 8 of the 10 “Mixed-Up Toys”. So instead of 10 x 10 x 10 = 1000 Silly Things, you will only be able to make 8 x 8 x 8 = 512 Silly Things. The program has been written in such a way that it will not crash if its images are not present.
I don’t think I can be any clearer than that, but review the source code if you want to know more.
That’s about it!
All the Best,
Bill Buckels
January 17, 2015