http://www.appleoldies.ca/azgraphics33/index.htm
Please visit the above link.
Single and Double
Lo-Res and Hi-Res Graphics in the
C Programming Language
Introduction to to Graphics in C on the Apple II
The purpose of this page is to share some of my routines and working
programs for those of you who are interested in Apple II Graphics in
C. When I first started developing in C on the Apple II back in the
1980's, I expanded the ProDOS graphics library that came with my
compiler (the Aztec C Apple II C65 Cross-Development Environment for
MS-DOS).
Recently I expanded the Apple II DOS 3.3 graphics library for this
same compiler creating a simpler DOS 3.3 equivalent of my ProDOS
graphics library (which I call the "G3" library for DOS 3.3). The
first releases of the "G3" library provided support for Apple II Hi-
Res Mode Graphics only.
Having devoted most of my Apple II C language graphics programming
during that time back in the 1980's exclusively to Hi-Res mode, I
decided recently (December 2009) to explore some of the other graphics
modes available... Lo-Res, Double Lo-Res and Double Hi-Res modes. I
stopped short of the additional "secret" modes.
As a result I have put-together an Apple II DOS 3.3 Aztec C compiler
distribution for MS-DOS and Windows based both on my recent and past
work with Apple II Graphics which includes an updated "G3" link
library and a reasonably large number of demo programs. These demo
programs are detailed on this page and hopefully will prove
informative to the novice and expert alike (although nothing presented
here is particularly new or mind-boggling). But this is not just some
lame attempt at a DOS 3.3 target environment either... DOS 3.3 Text
and Binary File Operations like reading "slide-show" scripts and
loading and saving Apple II Images are also supported by this compiler
and the "G3" library making all of this reasonably effective to
actually do something.
These programs listed here with working disk images, source code and
the compiler are up for download at the Aztec C website:
Download Apple33 Aztec C DOS 3.3 Distribution
http://www.aztecmuseum.ca/Apple33.zip
Note: Everything on this page is included in the above distribution
including Apple II diskimages of all programs, the source code and
library routines for all programs, and of course the cross-compiler
and the build environment pre-configured to produce these as well as
your own Apple II DOS 3.3 Aztec C programs from the comfort of your
Windows or MS-DOS compatible computer or favourite MS-DOS emulator.
The idea here is to learn by example without wading through mounds of
"fakeware" technical documents.
In all of this I also dabble with vector graphics, but much of my work
focuses around converting bitmapped graphics between different
platforms and the Apple II (and working with these under DOS 3.3).To
this end I have written and provide several utility programs which are
also part of this distribution and which are also discussed here.
Several of the Utility programs for converting from PC Bitmapped
Graphics to Apple II Bitmapped Graphics are written in 16 bit
Microsoft C for MS-DOS. I have been using that compiler for over 20
years to write simple graphics conversion utilities with excellent
results. To use these Utility programs run in an MS-DOS emulator like
DOSBox if you can't run them raw.
If you are lucky enough to be a Windows User your best bet to handle
the various archives here is Andy McFadden's CiderPress and if you are
lucky enough to be a Windows User, the DOS 3.3 compiler distribution
is provided with shortcuts to make DOS 3.3 development a very simple
matter (as it is with the other Aztec C compilers that I provide
through the Aztec C Museum website). If you are not a Windows User you
have my deepest sympathy, and note that if absolutely necessary this
compiler runs under Unix in an MS-DOS Emulator, and although I much
prefer Windows, I have occasionally held my nose and run this under
Ubuntu without problems.
Finally, the demos provided here work nicely under Windows in the
AppleWin Emulator (as well as the emulator that comes with Apple II
Oasis) at their highest speeds. I have also run these on my real
Apple //e with the 8 MHZ Zip Chip where they also work nicely.
I should also note that I tested the Double Lo-Res and Double Hi-Res
graphics on both a composite monitor and an rgb monitor on my real
Apple //e. The composite monitor gave me the best results between the
two, although the AppleWin and Apple II Oasis emulators gave me the
best results of all (albeit the fact that the emulators are not
real:). And I made a strange discovery on my real Apple //e... after
running Double Lo-Res or Double Hi-Res programs, if I didn't turn-off
the Double Res mode, and then I attempted to display Hi-Res graphics
afterwards in some other program, and even if I had rebooted, the rgb
monitor will display "garbage" but the composite monitor will display
Hi-Res graphics in some strange and undocumented reduced color palette
which looks ok but doesn't work quite right. The moral here is to
clean-up when done:)
So without much further ado, please review and enjoy the code listings
on this website and feel free to download and work with this DOS 3.3
cross-compiler and its many demos and utilities. One final note... it
would be a simple matter to port these routines from DOS 3.3 to ProDOS
8... not much of a port really... but that exercise is left for the
reader or for another day:) as is porting to other compilers which may
create faster programs but don't do everything that Aztec C does.
A word or two of explanation... cc65 has become a popular compiler on
the Apple II and although at time of this writing no DOS 3.3 file
operations are supported directly in cc65, one can always add this
sort of thing. cc65 seems to produce faster code than Aztec C and
although my preference is biased towards Aztec C I am also providing
disk images of working Lo-Res demos in both compilers for those of you
who may be interested (even in porting code from Aztec C to cc65).
Happy New Year and Have Fun!
Bill Buckels
bbuc...@mts.net
January 2010
> For your perusal and viewing pleasure I am pleased to announce the
> following addition to the AppleOldies website:
>
> http://www.appleoldies.ca/azgraphics33/index.htm
Well done! I've added links back to your site:
<http://home.roadrunner.com/~jbmatthews/apple2.html#rp1c>
--
John B. Matthews
trashgod at gmail dot com
<http://sites.google.com/site/drjohnbmatthews>
Ok then... here's another cc65 demo: Rod's Color Pattern for Hi-Res
Mode
http://www.appleoldies.ca/azgraphics33/rodhicc.htm
The cc65 demos that I have provided do not use any drivers and will
run either in ProDOS or DOS 3.3 without modification.
Bill
> On Jan 6, 8:55 pm, "John B. Matthews" <nos...@nospam.invalid> wrote:
> >
> > Well done! I've added links back to your site:
> >
> > <http://home.roadrunner.com/~jbmatthews/apple2.html#rp1c>
> >
> Hi Dr. John,
>
> Ok then... here's another cc65 demo: Rod's Color Pattern for Hi-Res
> Mode
>
> http://www.appleoldies.ca/azgraphics33/rodhicc.htm
Excellent!
> The cc65 demos that I have provided do not use any drivers and will
> run either in ProDOS or DOS 3.3 without modification.
And both optimize by using a base address lookup table. I wanted to
compare your approach with an example of using static linking in cc65; I
had the link, but forgot the example!
<http://home.roadrunner.com/~jbmatthews/a2/cross.html#sec5>
Both give a single-load binary and yours is fast and cross-OS. I would
say the results are exemplary. Thanks!
The lookup table trick avoided many calculations for me over the last
30 years. I highly recommend precalculating everything that one can.
This is probably just common-sense to most of us but I appreciate that
you have recognized how this technique can be applied to saving
precious instructions on these tiny processors:)
>
> Both give a single-load binary and yours is fast and cross-OS. I would
> say the results are exemplary. Thanks!
>
When one is using tiny processors (not only old C compilers) optimal
techniques seem to become honed razor-sharp as we also know. A good
example is the ballistic speed that cc65 works when compared to Aztec
C which was pretty damned good by the end of its life cycle.
I just wanted to post the alternative to that lookup table that you
refer to (see below)so I remember to make a note of this somewhere...
too many instructions... that's the thing for us to remember... this
is actually part of the code that I used in 1980-something to create
the table for the hi-res display:
/* provides base offset for hires scanlines */
/* does not bother to check whether we are in range */
/* gets the address as quickly as possible */
/* stays away from processor intensive mul and div */
gethibase(currentline,currentbase)
int currentline;
int *currentbase;
{
FILE *fp;
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;
/*
fp = fopen("hb.txt","a");
fprintf(fp,"%d,", *currentbase);
if (currentline%8 == 0) fprintf(fp,"\n");
fclose(fp);
*/
}
Having Fun,
Bill
And tables are an excellent example of the time-space tradeoff
in action. If space is more important than speed, calculation is
good.
I suspect the "base calculation" is both smaller and faster in
assembly language:
* Hi-res base address calculation. This comes from
* the HPOSN routine at $f411.
*
* Put the line in A. The result is placed in $26-27,
* offset from address 0. X and Y are not disturbed.
*
* You must OR $20 or $40 into $27 to select the hi-res
* page.
bascalc pha
and #$c0
sta hbasl
lsr
lsr
ora hbasl
sta hbasl
pla
sta hbash
asl
asl
asl
rol hbash
asl
rol hbash
asl
ror hbasl
lda hbash
and #$1f
sta hbash
rts
(From Andy McFadden's fast graphics routines, taken in turn from
the Applesoft HPOSN routine.)
Bit fiddling of this kind has always been the "poster child" for
high-level language inefficiency, though it is seldom needed in
practice. In Apple II graphics, it finds its way into the inner
loop (unless two pages of memory are used for a table)!
-michael
NadaNet and AppleCrate II: parallel computing for Apple II computers!
Home page: http://home.comcast.net/~mjmahon
"The wastebasket is our most important design
tool--and it's seriously underused."
I have a confession to make... I prefer table lookup to calculation
for the same reason I prefer C to both assembly language and BASIC...
readability
Other good reasons for table lookup besides readability have justified
my preference for readability for several decades.
To a BASIC programmer C is often considered unreadable. I can relate.
When I am in an ornery mood BASIC with line numbers is spaghetti no
matter how well written.
After some thought I posted this last message of mine into
comp.sys.apple2.programmer only since I doubt if the majority find it
readable no matter what:)
Bill
True, and Woz made perhaps the best effort in Disassembly Lines:
http://www.txbobsc.com/aal/1986/aal8612.html#a9
Cheers,
Nick.
Beautiful!
I think I see your point.
But consider that the addresses in the table seem quite magical and
hard to relate to the function of the table, since they encapsulate
the unusual video address mapping of the Apple II.
In just the same way, the short BASECALC routine encapsulates the
video architecture, in just one place. Everwhere it is used, it
is a call to BASECALC (or a macro expansion), which accomplishes
exactly the same level of readability.
I have used both BASECALC and tables, I'm just pointing out that
the structural abstraction is the same in both cases.
Bill-
I am impressed with the effort that went into these demos. I thought
I woke up in an alternate reality when I saw the cc65 versions ;-)
I had not seen this version either. Wow. Time to revisit this
routine again. Both the calculated address and table lookup address
routines have their place. Based on the type of graphics operation
I'm performing, I will decide to use the calculated address if it is
only a small percentage of the total time. Fill routines will
generally get the calculated version. Line drawing will generally get
the table lookup. Of course the line address calculation is only half
the equation. Let's not forget the divide-by-7 that is needed for
edges and horizontal offset. Again, there is the table vs.
calculation tradeoff. Fast hires graphics on the Apple II is one of
the most intriguing problems I've encountered in graphics programming.
Dave...
My complaint with cc65 is primarily that it's not as easy for me to
use as Aztec C. I am still trying to wrap my head around the linker
and there are many things that I dislike outright like the way inline
assembly has been implemented.
However, the optimizer seems quite good and the compiler seems to
produce tighter and smaller code than Aztec C which is pretty hard to
argue with.
Later,
Bill
It *is* a bit odd, considering the 'normal' way seems easier to
implement. There's always external linking though I and I tend to find
things are either so small just leave em in C or big enough to
externally link but different strokes...
> However, the optimizer seems quite good and the compiler seems to
> produce tighter and smaller code than Aztec C which is pretty hard to
> argue with.
I suspect it's easier when you don't have floating point types to
worry about.
Welcome back, BTW
Matt
Hi Matt,
I think there is more to cc65's efficiency over Aztec C than merely
this small difference... subtleties... not that it really matters in
the grand scheme of things since the bulk of non-trivial Apple //e
development in C world-wide is so miniscule as to be less than
insignificant and I am but a fly on the leg of an elephant.
Q - What happened to the fly who sat on the elephant's leg?
A - He got pissed-off!
That notwithstanding, there are very few developers that have
experience with both compilers and I am determined that I will make
some effort to bring my knowledge level on cc65 close to my level on
Aztec C if for no other reason than to be able to point-out that
cc65's handling of inline assembly is not to my liking. Perhaps when I
know more it will be to my liking.
For example, how would I do the following in cc65 using inline
assembly? See both links below. Please rewrite in entirety using cc65,
test and debug, and repost to this thread for discussion if you will,
starting with this:
http://www.appleoldies.ca/azgraphics33/lores.c.txt
The link above uses Aztec C's zero page pseudo-registers which are
"always-on".
The total immediate goal here with your expert help on the above is to
rewrite the following program in its entirety in cc65 (not simply Dr.
John's portion which comprises Rod's Color Pattern):
http://www.appleoldies.ca/azgraphics33/lodemo.htm
Thanks. If this works out perhaps you can help me decipher cc65's
linker also. I want to avoid the hi-res page altogether for my hi-res
and double hi-res graphics stuff so I can port my Aztec C programs to
cc65 (not just some puny demos).
It may be something missing in the translation to English but the cc65
documentation is not easy for me to read and I need clear examples
like full rewrites of my Aztec C stuff for this to make sense to me.
RTFM is just not an acceptable answer... (gauntlet down:)
It would probably be a good idea for you to install Aztec C as well as
cc65 just to C what I C.
Bill
>Thanks. If this works out perhaps you can help me decipher cc65's
>linker also.
At least I can try...
>I want to avoid the hi-res page altogether for my hi-res
>and double hi-res graphics stuff so I can port my Aztec C programs to
>cc65 (not just some puny demos).
I see.
If a program is small enough to just live with the memory above $4000
then the only thing to do is to link with the option
--start-addr $4000
as described in http://www.cc65.org/doc/apple2-7.html#ss7.1.
If a program is however larger then things get a bit more
complicated...
The cc65 linker doesn't allow to split segments into several memory
areas. So if you want to make use of both memory areas
1.) $0803-$1FFF
2.) $4000-$BEFF
then you need to do "something" manually one way or another.
As 1.) is the smaller area in most cases the best way is to find
"something" in your program
a) of significant size
b) but smaller than 1.)
to explicitly put in 1.) and have 2.) being the default for "all the
rest".
The easiest way is described in
http://www.cc65.org/doc/apple2-7.html#ss7.1:
"In memory constrained situations the memory from $803 to $1FFF can be
made available to a program by calling _heapadd ((void *) 0x0803,
0x17FD); at the beginning of main(). Doing so is beneficial even if
the program doesn't use the the heap explicitly because loading the
driver (and in fact already opening the driver file) uses the heap
implicitly."
The latter sentence refers to the fact that the cc65 Apple2 C library
by default allocates the ProDOS 1kB IOBuffer (necessary for every
opened file) on the heap.
If addional heap memory doesn't help your program then you need to
"make the linker aware" of the memory area 1.) in order to allow him
to place "something" there. This requires a custom linker config
file...
If your program can benefit from global/static variables placed in 1.)
then things are relatively simple as the BSS segments isn't loaded
from disk.
If however your program needs more room for code (or initialized data
or constants) then your have to have the linker generate an additional
file with the content to be placed at 1.).
You need to make sure that nothing is placed there which is esential
to starting up the program and have the file loaded explicitly via C
library calls from within your main(), which are - as you know - only
available for ProDOS 8 but not DOS 3.3.
If however DOS 3.3 is (one of) the desired environment(s) then the
only easy way I see is a BASIC / EXEC loader with a BLOAD and a BRUN
statement.
The explicit file loading via the C library has on the hand the
benefit of being already the same thing one does for (what I here just
call) overlays with cc65. The area 1.) might be a reasonably sized
overlay area. For the sake of simplicity I'd recommend to to only
overlay code but not initialized data or constants.
There are two ways to tell the compiler to actually place "something"
in your custom segment(s) living in the memory area 1.). Both are
described in http://www.cc65.org/doc/apple2-3.html:
"However code is never automatically placed there. Rather code needs
to be explicitly placed in the Language Card either per file by
compiling with --code-name HIGHCODE or per function by enclosing in
#pragma codeseg (push, "HIGHCODE") and #pragma codeseg (pop)."
The HIGHCODE segment described above can contain code automatically
moved to the Language Card by the C library startup code. However the
option/pragma is the same for any code segment you invent to make use
of the memory are 1.).
Using the language card very easy with cc65. Maybe you want to check
it out, I see primarily three scenarios:
- Using $D400-$DFFF: For programs running under plain vanilla ProDOS 8
quiting normally.
- Using $D000-$DFFF: For programs running under plain vanilla ProDOS 8
doing file I/O but rebooting on exit.
- Using $D000-$FFFF: For programs running under "unmoved" DOS 3.3 or
ProDOS 8 not performing file I/O and rebooting on exit.
If you answer me here, which of the approaches above (incl. LC usage)
best fits your needs I'll provide you with additional information -
i.e. a custom linker config if necessary.
It would benefitial to know more about the desired environment to
program is to run in. Currently I presume a BIN file with start
address $4000.
Best, Oliver
> For example, how would I do the following in cc65 using inline
> assembly? See both links below. Please rewrite in entirety using cc65,
> test and debug, and repost to this thread for discussion if you will,
> starting with this:
>
> http://www.appleoldies.ca/azgraphics33/lores.c.txt
Now you've lost me Bill. Didn't you already port them ?
> It may be something missing in the translation to English but the cc65
> documentation is not easy for me to read and I need clear examples
> like full rewrites of my Aztec C stuff for this to make sense to me.
> RTFM is just not an acceptable answer... (gauntlet down:)
How about RTFO, as in 'output' of compiler.
>For example, how would I do the following in cc65 using inline
>assembly? See both links below. Please rewrite in entirety using cc65,
>test and debug, and repost to this thread for discussion if you will,
>starting with this:
>
>http://www.appleoldies.ca/azgraphics33/lores.c.txt
As it was already pointed out using inline assembly in cc65 isn't very
common:
- If you have (nearly) assembly-only functions like in your URL above
then the ca65 assembler is just the by far better tool.
- If you have C functions with little assembly mixed-in then you
suffer from not being able to optimize the C part of the function.
For the latter see http://www.cc65.org/doc/cc65-9.html:
"Note: Inline assembler statements are subject to all optimizations
done by the compiler. There is currently no way to protect an inline
assembler statement from being moved or removed completely by the
optimizer. If in doubt, check the generated assembler output, or
disable optimizations."
Anyway for the sake of demonstration how the cc65 inline assmbler
works I did as you asked above (see below). Some comments:
- The Apple2 C library keeps in general the LC switched in as
documented in http://www.cc65.org/doc/apple2-3.html. So you need to
switch in the ROM temporarily in order to call the LoRes functions.
- It would have been possible to stay closer to the original code
(which copies the parameters for easier access from the inline
assembly) but the cc65 inline assembler offers a clean way to access
parameters (and local variables) directly so I made use of it,
although it makes the _inline_ code look more complicated.
- I haven't tested all functions but I was however able to reproduce
the screen you show at
http://www.appleoldies.ca/azgraphics33/lodemo_002.png
with the code below.
- The original code has a bug: lovlin() places y2 in $002c instead of
$002d.
- I'd certainly store x2/y2 to $2c/$2d using (inline) assembly but
kept the code here closer to the original one to have at least a
little C code left.
- You can of course switch of optimization globaly or for several
functions at once. The wrapping of every function containing inline
assembly in its own set of pragmas is just my personal style.
- As commented below even functions prototyped to return an 8 bit
value need to in fact return a valid 16 bit value. This is related to
the implicit type promotion rules of the C language.
Best, Oliver
P.S.: Just for the sake of completeness... The cc65 Apple2 C library
comes with a TGI driver for lores graphics containing more or less the
code below.
====================
#define SWITCH_LC2 asm("BIT $C080")
#define SWITCH_ROM asm("BIT $C082")
#pragma optimize (push,off)
void setcolor(unsigned char value)
{
asm("LDY #%o", value);
asm("LDA (sp),Y"); // Sets the plotting color to N, 0 <= N <= 15
SWITCH_ROM;
asm("JSR $F864");
SWITCH_LC2;
}
#pragma optimize (pop)
#pragma optimize (push,off)
void loplot(unsigned char x, unsigned char y)
{
asm("LDY #%o", y);
asm("LDA (sp),Y");
asm("PHA");
asm("LDY #%o", x);
asm("LDA (sp),Y");
asm("TAY"); // Lo-Res Plot X (Horizontal) Coordinate (0-39)
asm("PLA"); // Lo-Res Plot Y (Vertical) Coordinate (0-39)
SWITCH_ROM;
asm("JSR $F800");
SWITCH_LC2;
}
#pragma optimize (pop)
#pragma optimize (push,off)
unsigned char getlocolor(unsigned char x, unsigned char y)
{
asm("LDY #%o", y);
asm("LDA (sp),Y");
asm("PHA");
asm("LDY #%o", x);
asm("LDA (sp),Y");
asm("TAY"); // Lo-Res Plot X (Horizontal) Coordinate (0-39)
asm("PLA"); // Lo-Res Plot Y (Vertical) Coordinate (0-39)
SWITCH_ROM;
asm("JSR $F871"); // Result (0-15) in Accumulator
SWITCH_LC2;
asm("LDX #$00"); // Necessary even for 8 bit retval (!)
return __AX__;
}
#pragma optimize (pop)
#pragma optimize (push,off)
void lohlin(unsigned char y, unsigned char x1, unsigned char x2)
{
/* Rightmost X Coordinate (0-39) */
/* Store it at H2 Lo-res line end-point */
*(unsigned char *)0x2c = x2;
asm("LDY #%o", y);
asm("LDA (sp),Y");
asm("PHA");
asm("LDY #%o", x1);
asm("LDA (sp),Y");
asm("TAY"); // Leftmost X Coordinate (0-39)
asm("PLA"); // Y Coordinate (0-47)
SWITCH_ROM;
asm("JSR $F819");
SWITCH_LC2;
}
#pragma optimize (pop)
#pragma optimize (push,off)
void lovlin(unsigned char x, unsigned char y1, unsigned char y2)
{
/* Bottom Y Coordinate (0-47) */
/* Store it at V2 Lo-res line end-point */
*(unsigned char *)0x2d = y2;
asm("LDY #%o", y1);
asm("LDA (sp),Y");
asm("PHA");
asm("LDY #%o", x);
asm("LDA (sp),Y");
asm("TAY"); // X Coordinate (0-39)
asm("PLA"); // Top Y Coordinate (0-47)
SWITCH_ROM;
asm("JSR $F828");
SWITCH_LC2;
}
#pragma optimize (pop)
void lobox(unsigned char x1, unsigned char y1,
unsigned char x2, unsigned char y2, unsigned char color)
{
setcolor(color);
lohlin(y1, x1, x2);
y1++;
y2--;
lovlin(x1, y1, y2);
lovlin(x2, y1, y2);
y2++;
lohlin(y2, x1, x2);
}
> - The original code has a bug: lovlin() places y2 in $002c instead of
> $002d.
There is no bug in my code. The very fact that my code works properly
would seem to indicate that further review might be indicated since
working disk images and everything else are available from my website,
although I do appreciate you throwing all caution to the wind and
risking accusations of reckelessness in the merry haste of your
enthusiastic response:) I thank you heartily for all the good thoughts
but for this bug in your message...
I am fortunately more careful. You have read my code incorrectly and
y2 is in fact placed in 0x2d... here's the code snippet (below):
If you examine the eolptr which points to 0x2c you will note that
since the C language allows subscripts to be used with pointers and
since y2 is assigned to a subscripted address and not directly to this
pointer's base address, the ordinal constant "YREG" determines the
actual assignment address which is offset by 1 byte to 0x2d:
unsigned char *eolptr = (unsigned char *)0x2c;
#define XREG 0
#define YREG 1
lovlin(x, y1, y2)
{
/* load parameters into user regs */
byteregptr[XREG] = x;
byteregptr[YREG] = y1;
/* Bottom Y Coordinate (0-47) */
/* Store it at V2 Lo-res line end-point */
eolptr[YREG] = y2;
/* make ml call */
#asm
LDY XVAL ; X Coordinate (0-39)
LDA YVAL ; Top Y Coordinate (0-47)
JSR $F828
#endasm
}
I actually enjoyed the elegance of this little piece and congratulated
myself many times:).
However the "bug" that you have mistakenly attributed to my
programming does in fact exist as an error in various documentation
including the Apple2 programming FAQ which has appeared in this
newgroup over the years.
The error apparently started at the following link and has appeared
all over the web, but let me assure you that my code does not suffer
from this same affliction:
http://www.textfiles.com/apple/peex-pokes.txt
Thanks again for all the good info.
Bill
Hi Oliver,
I will try the various approaches that you suggest (starting with the
above) and thanks again for all the good info. This should keep me
busy for awhile.
Bill
> >http://www.appleoldies.ca/azgraphics33/lores.c.txt
>
> Now you've lost me Bill. Didn't you already port them ?
>
See Oliver's response... and no I didn't port much and I documented
what I did and didn't document what I didn't do but I think I may have
already done more with cc65 than at least one lost soul even though I
haven't really done a thing yet:)
I'm off to try some of Oliver's stuff and have some more fun.
Bill
> unsigned char *eolptr = (unsigned char *)0x2c;
>
> #define XREG 0
> #define YREG 1
>
> lovlin(x, y1, y2)
> {
> /* load parameters into user regs */
> byteregptr[XREG] = x;
> byteregptr[YREG] = y1;
> /* Bottom Y Coordinate (0-47) */
> /* Store it at V2 Lo-res line end-point */
> eolptr[YREG] = y2;
> /* make ml call */
> #asm
> LDY XVAL ; X Coordinate (0-39)
> LDA YVAL ; Top Y Coordinate (0-47)
> JSR $F828
> #endasm
>
> }
>
> I actually enjoyed the elegance of this little piece and congratulated
> myself many times:).
On Jan 14, 8:31 am, Bill Buckels <bbuck...@escape.ca> wrote:
> unsigned char *eolptr = (unsigned char *)0x2c;
>
> #define XREG 0
> #define YREG 1
>
> lovlin(x, y1, y2)
> {
> /* load parameters into user regs */
> byteregptr[XREG] = x;
> byteregptr[YREG] = y1;
> /* Bottom Y Coordinate (0-47) */
> /* Store it at V2 Lo-res line end-point */
> eolptr[YREG] = y2;
> /* make ml call */
> #asm
> LDY XVAL ; X Coordinate (0-39)
> LDA YVAL ; Top Y Coordinate (0-47)
> JSR $F828
> #endasm
>
> }
>
> I actually enjoyed the elegance of this little piece and congratulated
> myself many times:).
Not bad, but remember that by accessing locations through a pointer
variable is going to cause the compiler (assuming cc65) to:
a) copy the pointer to two consecutive zero page locations (in cc65's
case, labelled ptr)
b) use (zp),y addressing to set the value of what we know to be a zero
page location.
Unfortunately, cc65 doesn't optimize accesses via constant pointers,
otherwise adding a const following your * would achieve this for you.
Fortunately, cc65 provides an alternative:
extern unsigned char x;
extern unsigned char y;
#pragma zpsym("x")
#pragma zpsym("y")
(...)
x = 5;
y = 7;
This will result in:
lda #$05
sta _x
lda #$07
sta _y
Of course, it's up to you to provide the value of the x and y symbols
in an extra (assembly language) file but this results in code that is
ostensibly optimal. I leave it as an exercise for you to examine the
code generated by your own approach.
I should also be noted that if you're calling a machine language
routine that uses only memory based parameters (not in this case), you
can do it with a function pointer:
void (* const somefunc)(void) = (void*)0xf828;
... and then simply call it.
Unfortunately cc65 also won't recognise that this pointer is constant,
and will copy it to the zeropage and do an indirect jump.
I would think however that in graphics routines performance would be
paramount, so I strongly urge you to follow Oliver's and my advice and
implement these using the assembler and link them in.
Matt
> There is no bug in my code.
I repeat!!! Not only that, but websites, demos, and a compiler
distribution are built around the properly working code.
> However the "bug" that you have mistakenly attributed to my
> programming does in fact exist as an error in various documentation
If anyone wants to look at the bug that Oliver didn't find in my code,
the apple does not fall far from the tree:)
http://www.textfiles.com/apple/peeks.pokes.2
http://www.textfiles.com/apple/peex-pokes.txt
http://www.textfiles.com/apple/peekpoke.app
These 3 documents and others have propagated this misinformation
around the planet by either copying the bug forward or linking to it.
I must confess that I too was initially misled by this BS and blind-
copying of misinformation without a SH*TSTORM test to determine if the
document is merely toilet-paper.
The SH*TSTORM test did in fact confirm as toilet-paper so I avoided
the pitfall from the grace of the C god.
I would further suggest that anyone with links pointing to these BS
documents should warn the reader that the document is incorrect and
therefore any information cannot be trusted. The responsible thing for
website owners hosting these BS documents and others like them would
likely be to either correct and test the document in its entirety or
remove it.
Again, rumours of this bug in my code are dangerously and highly
exagerrated but beware of false documentation and false prophecies.
Use the source Luke! Working programs don't lie.
TANSTAAFL and get your eyes checked regularly to be on the safe-side,
Bill
> Again, rumours of this bug in my code are dangerously and highly
> exagerrated but beware of false documentation and false prophecies.
> Use the source Luke! Working programs don't lie.
Indeed. The source of the routine at $F828 reveals all, in less than
10 seconds at a monitor prompt, which shows how often people look up
such things on such web resources.
> TANSTAAFL and get your eyes checked regularly to be on the safe-side,
The Moon is a harsh mistress, but loneliness might've tamed her.
> http://www.textfiles.com/apple/peeks.pokes.2
> http://www.textfiles.com/apple/peex-pokes.txt
> http://www.textfiles.com/apple/peekpoke.app
>
> These 3 documents and others have propagated this misinformation
> around the planet by either copying the bug forward or linking to it.
AFAIK, the earliest instance of the erratum appears in the original
"Apple Monitors Peeled." It correctly describes the location used by
VILNE; but the label was inadvertently left blank, leading a later
generation of unwary scribes into heresy. See page 5.
<http://www.apple-2.com/pdf/a2mp.pdf>
Of course, the Apple II monitor listing is unambiguous.
>> - Using $D400-$DFFF: For programs running under plain vanilla ProDOS 8
>> quiting normally.
>I will try the various approaches that you suggest (starting with the
>above) and thanks again for all the good info. This should keep me
>busy for awhile.
If you're interested in that Language Card usage topic you might want
to check out http://www.cc65.org/doc/apple2-4.html which describes the
various Apple2 linker configs coming with cc65.
Best, Oliver
You are right! I didn't recognize that you were using an array to
produce a one byte offset.
>The very fact that my code works properly
>would seem to indicate that further review might be indicated since
>working disk images and everything else are available from my website
To be totally honest I assumed that you intentionally inserted that
bug into the source code before uploading it to make sure that your
request "Please rewrite in entirety using cc65, test and debug" was
really followed in all three aspects.
>Thanks again for all the good info.
You're welcome, Oliver
>I should also be noted that if you're calling a machine language
>routine that uses only memory based parameters (not in this case), you
>can do it with a function pointer:
>
>void (* const somefunc)(void) =3D (void*)0xf828;
>
>... and then simply call it.
This scheme can actually be extended to machine language routines that
a) use A as 8 bit parameter or AX as 16 bit parameter when declaring
the function pointer as __fastcall__
(see http://www.cc65.org/doc/cc65-5.html).
b) return an 8 bit value in A or a 16 bit value in AX (see
http://www.cc65.org/doc/internal.txt).
Best, Oliver
Good point.
> The Moon is a harsh mistress, but loneliness might've tamed her.
A lonely loonie eh? Canada's $1.00 coin is called a "loonie" after the
bird and not anything about lunar inhabitants.
Bill
Interesting. How does one declare a function pointer as having
fastcall semantics? I couldn't get it to work.
Matt
> A lonely loonie eh? Canada's $1.00 coin is called a "loonie" after the
> bird and not anything about lunar inhabitants.
Ours are usually just called a 'buck'. The dollar bill and two dollar
bill where phased out in favour of coins in '84 and '88 respectively.
I personally prefer the greenback style though. It 'feels' like it's
worth more, and compared to $AUD it almost always is :-)
>> a) use A as 8 bit parameter or AX as 16 bit parameter when declaring
>> the function pointer as __fastcall__
>> (seehttp://www.cc65.org/doc/cc65-5.html).
>Interesting. How does one declare a function pointer as having
>fastcall semantics? I couldn't get it to work.
Hm, it always worked for me out of the box. I just hacked up a small
example for you:
void (__fastcall__ *cout)(unsigned char c)
= (void (__fastcall__ *)(unsigned char))0xFDED;
void main (void)
{
__asm__("bit $C082");
cout('H' | 0x80);
cout('e' | 0x80);
cout('l' | 0x80);
cout('l' | 0x80);
cout('o' | 0x80);
cout('\r' | 0x80);
__asm__("bit $C080");
}
Best, Oliver
Ah. Thanks. I was putting the __fastcall__ modifier in the wrong spot
(beginning of statement)
Matt
>The cc65 linker doesn't allow to split segments into several memory
>areas. So if you want to make use of both memory areas
>1.) $0803-$1FFF
>2.) $4000-$BEFF
>then you need to do "something" manually one way or another.
>[...]
>If addional heap memory doesn't help your program then you need to
>"make the linker aware" of the memory area 1.) in order to allow him
>to place "something" there. This requires a custom linker config
>file...
>[...]
>You need to make sure that nothing is placed there which is esential
>to starting up the program and have the file loaded explicitly via C
>library calls from within your main(), which are - as you know - only
>available for ProDOS 8 but not DOS 3.3.
>
>If however DOS 3.3 is (one of) the desired environment(s) then the
>only easy way I see is a BASIC / EXEC loader with a BLOAD and a BRUN
>statement.
I've been thinking about this topic quite some more and finally came
up with a solution that I think matches your needs very well:
Instead of loading the content for memory area 1.) from disk via the C
library the "new" solution loads both memory areas from one large
file. The benefits:
- Runs both on DOS 3.3 and ProDOS 8,
- Avoids linking in the whole disk I/O code if disk I/O isn't required
for other tasks.
Loading memory area 1.) and 2.) in one step obviously means including
the 8kB of hires screen memory in the program file. But instead of 8kB
of zeros you can integrate a startup/splash/background screen in
program file :-)
The following cc65 linker config realizes the approach outline above:
====================
FEATURES {
STARTADDRESS: default = $0803;
}
MEMORY {
ZP: start = $0080, size = $001A, define = yes;
HEADER: start = $0000, size = $0004, file = "";
LOWRAM: start = %S, size = $4000 - %S, file = %O, fill = yes;
RAM: start = $4000, size = $5600, file = %O, define = yes;
MOVE: start = $0000, size = $FFFF, file = %O, define = yes;
LC: start = $D400, size = $0C00, define = yes;
}
SEGMENTS {
ZEROPAGE: load = ZP, type = zp;
EXEHDR: load = HEADER, type = ro;
STARTUP: load = LOWRAM, type = ro;
LOWCODE: load = LOWRAM, type = ro;
HIRES: load = LOWRAM, type = rw, optional = yes, start = $2000;
CODE: load = RAM, type = ro;
RODATA: load = RAM, type = ro;
DATA: load = RAM, type = rw;
ZPSAVE: load = RAM, type = bss, define = yes;
BSS: load = RAM, type = bss, define = yes;
INIT: load = MOVE, type = ro, define = yes, run = RAM;
HIGHCODE: load = MOVE, type = ro, optional = yes, run = LC;
}
FEATURES {
CONDES: segment = INIT,
type = constructor,
label = __CONSTRUCTOR_TABLE__,
count = __CONSTRUCTOR_COUNT__;
CONDES: segment = RODATA,
type = destructor,
label = __DESTRUCTOR_TABLE__,
count = __DESTRUCTOR_COUNT__;
CONDES: type = interruptor,
segment = RODATA,
label = __INTERRUPTOR_TABLE__,
count = __INTERRUPTOR_COUNT__;
}
SYMBOLS {
__STACKSIZE__: value = $0800, weak = yes; # 2k stack
}
====================
Notes:
- The linker config above needs to be saved to a linker config file
and used via the -C option of the ld65 linker (or cl65 utility).
- That config file does _NOT_ create the "usual" 4 byte DOS 3.3
header.
- With that config file you can place code in the memory area 1.) by
using the segment "LOWCODE" in the very same way you can place code in
the Language Card using the segment "HIGHCODE". Everything else goes
into memory area 2.).
In order to integrate a startup screen into the program file you need
a two-line assembler file like the following:
====================
.segment "HIRES"
.incbin "mypicture.pic"
====================
Notes:
- The assembler file should have the extension .s
- If you use the cl65 utility then you can simply place .s files
together with .c files on the same command line.
The next cc65 release will come with a linker config similiar to the
one above.
I didn't mention the usage of the cc65 graphics library (TGI) in this
approach as I presume that you're going to use your own one...
Good luck, Oliver
Hi Oliver,
Again, thanks for all your good work providng cc65 info on this thread
including this latest linker config. cc65 has some shortcomings
including its lack of file support for DOS 3.3 and its lack of
floating point support. Having said that, its performance and program
size is quite advantageous over Aztec C, and for that reason I am
intending to port my IBM-PC children's math program "Bingo the Number
Dog" to the Apple II ProDOS 8 using cc65 and perhaps double hi-res
graphics (I haven't yet decided if the reduced resolution vs. hi-res
will be suitable)...
Anyway, you presume correctly when you presume that I will use my own
graphics stuff...
The one thing I am not clear about... tell me about .SYS program
launchers... can I launch a program with a lower base address using
a .SYS program somehow in cc65... that way I wouldn't need an
AppleSoft Startup...
Bill
If you want ProDOS to launch you, it's going to load you up at $2000
whether or not you like it. cc65 doesn't have a say in that. In
assembly, I just do a little relocation at the head of the program
down to $800 (say) if I want to move lower. I also have to ORG my
assembly at $800 so the addresses end up making sense in their final
destinations. How that particular trick would work within C and a
linker I am not sure. But I imagine some chicanery with the start
address would do it, knowing that no matter what you tell the
toolchain, a SYS program is going to start out life at $2000.
I'm not sure what you mean by an "AppleSoft Startup." Any properly
formed SYS program can run on its own without any help from
Applesoft. If it's the first (or only) program on the boot disk with
a .system filename suffix, it will get loaded at $2000 and executed
immediately after ProDOS completes its initialization.
>Again, thanks for all your good work providng cc65 info on this thread
>including this latest linker config.
You're welcome :-)
>and for that reason I am
>intending to port my IBM-PC children's math program "Bingo the Number
>Dog" to the Apple II ProDOS 8 using cc65
Great - I'd really like to see this happening!
>Anyway, you presume correctly when you presume that I will use my own
>graphics stuff...
...and there's actually no technical issue with that whatsoever as the
cc65 "core" stuff (the one always ending up in a cc65 program) knows
nothing at all about the cc65 graphics library.
>The one thing I am not clear about... tell me about .SYS program
>launchers... can I launch a program with a lower base address using
>a .SYS program somehow in cc65... that way I wouldn't need an
>AppleSoft Startup...
If I understand you correctly then my "LOADER.SYSTEM - an Apple ][
ProDOS 8 loader for cc65 programs" is the very thing you're looking
for :-) Get it here:
ftp://ftp.musoftware.de/pub/uz/cc65/contrib/loader-1.4.zip
A quite detailed doc is here:
ftp://ftp.musoftware.de/pub/uz/cc65/contrib/loader-1.4.txt
A short overview:
- LOADER.SYSTEM is a tiny .SYS program (fits in one ProDOS block) that
reloactes parts of itself into page 3 and loads from there the actual
program.
- The actual program is a standard .BIN file.
- All cc65 programs (indenpendent from their type) dynamically detect
the presence of ProDOS but absence of BASIC.SYSTEM and then quit to
the ProDOS dispatcher instead of the usual BASIC warmstart.
- LOADER.SYSTEM knows the startup file protocol and passes the name as
argv[1] to the cc65 main() so you have a cmdline parameter with Davex
and similiar shells.
Best, Oliver
>If you want ProDOS to launch you, it's going to load you up at $2000
>whether or not you like it. cc65 doesn't have a say in that. In
>assembly, I just do a little relocation at the head of the program
>down to $800 (say) if I want to move lower. I also have to ORG my
>assembly at $800 so the addresses end up making sense in their final
>destinations.
My idea on this is somewhat different: I presume small programs don't
have issues with running from $2000. So we're talking about large
programs. But large program take quite some time to be moved in RAM.
So it's actually much faster to have a <512 byte ("seedling") ProDOS
SYSTEM file loading the large file right from disk to the final
destination. And beside that: Really large files simply can't be
relocated as they don't fit into RAM between $2000 and $BF00.
>I'm not sure what you mean by an "AppleSoft Startup." Any properly
>formed SYS program can run on its own without any help from
>Applesoft. If it's the first (or only) program on the boot disk with
>a .system filename suffix, it will get loaded at $2000 and executed
>immediately after ProDOS completes its initialization.
I imagine I understand Bill well. My LOADER.SYSTEM (see my other post)
is a .SYS file able to load .BIN files to about all locations. After
all that's what BASIC.SYSTEM (together with a one-line Applesoft
program with a BRUN statement) can do too. So I see LOADER.SYSTEM as a
BASIC.SYSTEM replacement (or rather improvement) for a very specific
scenario: Loading .BIN files.
Best, Oliver
> I'm not sure what you mean by an "AppleSoft Startup." Any properly
> formed SYS program can run on its own without any help from
> Applesoft. If it's the first (or only) program on the boot disk with
> a .system filename suffix, it will get loaded at $2000 and executed
> immediately after ProDOS completes its initialization.
I'd guess that he means using an Applesoft program to BRUN the
linked program. That would make it trivial to load at, say, $900.
-michael
NadaNet and AppleCrate II: parallel computing for Apple II computers!
Home page: http://home.comcast.net/~mjmahon
"The wastebasket is our most important design
tool--and it's seriously underused."
Thanks David but I am pretty well aware of all you have said but there
is always a chance that old age will set-in and I suppose I will
eventually forget.
See Oliver's reply. I was after his launcher but didn't know it:) I
should have remembered but didn't.
> I'm not sure what you mean by an "AppleSoft Startup." Any properly
> formed SYS program can run on its own without any help from
> Applesoft. If it's the first (or only) program on the boot disk with
> a .system filename suffix, it will get loaded at $2000 and executed
> immediately after ProDOS completes its initialization.
The main module of this proposed program is a BIN and not a SYS. BRUN
in a startup program works fine in ProDOS Applesoft. See Michael
Mahon's post.
Regards,
Bill
Thanks for that. I adapted LOADER.SYSTEM in a couple of different
ways to 1) have a little program selector, and 2) have little stub
loaders for my code (without any of the fancy parameter passing
stuff). My code had typically loaded at $2000, and then relocated
itself to $800. On slower machines, I can see the difference. Not to
mention I don't need to load (or ship) BASIC.SYSTEM any more.
Would you please make your changes available? I think Oliver's loader
is pretty nifty and I am using it as-is over here. BTW it doesn't work
with Aztec C so I am playing at writing a similar loader for Aztec C
while at the same time I organize my cc65 Project for my "Bingo The
Number Dog" port to the Apple II.
Bill
Sure, everything in all its embarrassing glory is in CVS for all to
see. Note that Oliver's program is standalone, while mine is
integrated into the larger ADTPro ecosystem and relies on a few
elements present there. I didn't have any luck with the IIe/II+ un-
uppercasing COUT routine that was in Oliver's version, so I simplified
that as well.
The program selector is here:
http://adtpro.cvs.sourceforge.net/viewvc/adtpro/adtpro/client/src/prodos/startup.asm?view=markup
And the bare-bones stub is here (I add .BIN as a suffix rather than
stripping off .SYSTEM):
http://adtpro.cvs.sourceforge.net/viewvc/adtpro/adtpro/client/src/prodos/launch.asm?view=markup