delaney@wnre.aecl.CDN.UUCP (06/11/87)
This note covers program conversion to the GS
Apple //GS Technical Note #2
Transforming Input/Output Subroutines for use in "Native" Mode.
Written by: Pete Mc Donald 10/86
________________________________________________________________________
This note outlines a number of techniques, useful in the transformation
of Apple // I/O subroutines for use in the "Native" Apple //GS
environment. Note: This note is only of interest to those who are
"converting" applications. If you intend to let your application remain
a "classic" Apple // application, then this information can be ignored.
________________________________________________________________________
The Apple //GS execution environment represents quite a departure from
the environment to which the average Apple // developer is accustomed.
This fact results in a number of unique problems when one attempts to
convert existing Apple // applications for use in the "native" Apple
//GS environment.
Some of the biggest conversion problems are with I/O subroutines that
contain critically timed code. The problems stem from two major issues.
Number one is that in the "Native" GS environment, one cannot guarantee
that there will be memory available in a given bank. Number two is
that I/O is not available in every bank.
There are actually a number of possible solutions to this problem.
Which ones you should use will depend on what exactly the program in
question is doing. In this note I will attempt to describe some of the
problem situations, and possible solutions.
Examine the following "6502" code segment. It serves no useful purpose,
other than to illustrate a simple manifestation of the problem. Assume
IoLoc is a location in the CXXX range of memory.
Loop Lda Ioloc
Dey
Bpl Loop
This fragment, if placed anywhere in banks 2-through "Available banks,"
will have no effect on the I/O device it is intended to control. This
is due to the fact that in those banks, CXXX contains RAM not I/O
circuitry.
There are 2 possible solutions in this case. Either change the
instruction Lda IoLoc so that it uses "Long" addressing, thereby
forcing the CPU to reference the the proper bank. (The problem with
this is that the "long" version of Lda requires an extra CPU cycle to
execute. If the code segment is timing critical, then this is likely to
be unacceptable.)
Alternately, and in the timing critical case ideally, we could instead
set the "databank" register before we enter the loop. The effect of
this is that the instruction Lda IoLoc would take the same number of
cycles as it did before, and the loop timing would not change.
Well this seems pretty easyI Unfortunately, most code is not isolated
like that in the example. Specifically, code will also commonly try to
load or store to some location in memory other than the I/O location at
the same time that it is trying to access the I/O location.
Take for example the following fragment:
Loop Lda Data,y
Sta IoLoc
Dey
Bpl Loop
In this case, we assume that the label "Data" refers to some kind of
table that normally resides in the same bank as the program. Now the
problem is that if the databank register is set to access I/O
locations, then obviously the reference to "Data" will end up
referencing the same bank as the I/O. This is not likely to be
acceptable. One thing that can be done, is to move the data table to
the direct page (zero page for 6502 programmers). Then the problem is
that the instruction lda data,y will take one less cycle than before.
There is a solution, although it is a little complicated. If we set the
direct page register to a "non page-aligned" location, then a 1 cycle
penalty is applied to all direct page references, and at least for this
example, we have solved our problem.
However, nothing is ever that simple. What happens to references to
other direct page locations that expect to operate without the one
cycle penalty? To really address this question would take a lot more
space that I have here. So, in lieu of further examples I will just
give some general info.
As an aside, I have used these techniques to transform the old "Apple
// Disk // formatter module," for use in any bank of memory in the
native GS environment. This was accomplished using, almost exclusively,
editor find and replace commands, and was done in a matter of hours
instead of the days that would have been required for a complete
rewrite of the program.
In addition to the techniques already mentioned, there were a couple of
other things that ended up being necessary to complete the
transformation.
As I mentioned above, one problem that comes up is what to do when you
have a program that references I/O, local "program-bank" data, and
zero-page. In this case, there are a couple of possible situations that
will require significant rewrites, but not necessarily.
(Diagram missing)
In the case of the disk formatter, it turned out that some modules used
both normal zp addressing, and normal 16 bit absolute indexed. Since
the transformation process dictates that we change 16 bit absolute
addressing to direct page addressing with a non page aligned direct
page, there could have been a problem, had both uses of the direct page
been timing critical. Fortunately, by treating each module of the
program separately, it worked out that when I needed both types, only
one was timing critical. The solution was in some modules, to set the
direct page to a non-page aligned value and in other modules, to set
the direct page to a page aligned value. There are also some minor
logistical issues about having a direct page whose base address can
either be at XXX0, or XXX1, the biggest of which is keeping track of
which is in effect at a given point, and knowing to reference the label
as either label, or label +1, or label P1 depending on the particular
case.
One last note. In the case of the formatter conversion, there was one
other fairly major issue. The problem is that there are not direct page
versions of all the modes of 16 bit absolute. For example, one cannot
convert 16bitaddress,X to 8bitAaddress,X. In the case of the formatter,
I was able to deal with it by reversing all the register use. (i.e. all
ldy become ldx, and all sty become stx, etc.,etcI
There are still a number of other ways that one can approach these
issues. In fact one that comes to mind would be to use some form of the
new stack-relative addressing modes to yield yet another range of
semi-independently accessible addresses.
The real point of this note is that, with a little bit of thought and
effort, one can successfully convert a large subset of likely
configurations for use in the native environment without major
rewrites. The bottom line is, be creative!
For more information on the various new modes and features of the
65816, I heartily recommend Programming the 65816 Including the 6502,
65C02, and 65802. (Eyes/Lichty) [Prentice Hall Press].