The content in internal.txt is now fully present in the Wiki.
This commit is contained in:
parent
88247613d2
commit
c9fcfe8549
2 changed files with 0 additions and 210 deletions
|
@ -82,9 +82,6 @@
|
||||||
<tag><htmlurl url="geos.html" name="geos.html"></tag>
|
<tag><htmlurl url="geos.html" name="geos.html"></tag>
|
||||||
The GEOSLib manual.
|
The GEOSLib manual.
|
||||||
|
|
||||||
<tag><htmlurl url="internal.txt" name="internal.txt"></tag>
|
|
||||||
A somewhat older text describing several cc65 internals.
|
|
||||||
|
|
||||||
<tag><htmlurl url="library.html" name="library.html"></tag>
|
<tag><htmlurl url="library.html" name="library.html"></tag>
|
||||||
An overview over the cc65 runtime and C libraries.
|
An overview over the cc65 runtime and C libraries.
|
||||||
|
|
||||||
|
|
207
doc/internal.txt
207
doc/internal.txt
|
@ -1,207 +0,0 @@
|
||||||
|
|
||||||
|
|
||||||
Internals doc for CC65
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
Stacks:
|
|
||||||
-------
|
|
||||||
|
|
||||||
The program stack used by programs compiled with CC65 is located in high
|
|
||||||
memory. The stack starts there and grows down. Arguments to functions, local
|
|
||||||
data etc are allocated on this stack, and deallocated when functions exit.
|
|
||||||
|
|
||||||
The program code and data is located in low memory. The heap is located
|
|
||||||
between the program code and the stack. The default size for the parameter
|
|
||||||
stack is 2K, you may change this for most platforms in the linker
|
|
||||||
configuration.
|
|
||||||
|
|
||||||
Note: The size of the stack is only needed if you use the heap, or if you
|
|
||||||
call the stack checking routine (_stkcheck) from somewhere in your program.
|
|
||||||
|
|
||||||
When calling other functions, the return address goes on the normal 6502
|
|
||||||
stack, *not* on the parameter stack.
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
Registers:
|
|
||||||
----------
|
|
||||||
|
|
||||||
Since CC65 is a member of the Small-C family of compilers, it uses the notion
|
|
||||||
of a 'primary register'. In the CC65 implementation, I used the AX register
|
|
||||||
pair as the primary register. Just about everything interesting that the
|
|
||||||
library code does is done by somehow getting a value into AX, and then calling
|
|
||||||
some routine or other. In places where Small-C would use a secondary
|
|
||||||
register, top-of-stack is used, so for instance two argument function like
|
|
||||||
integer-multiply work by loading AX, pushing it on the stack, loading the
|
|
||||||
second value, and calling the internal function. The stack is popped, and the
|
|
||||||
result comes back in AX.
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
Calling sequences:
|
|
||||||
------------------
|
|
||||||
|
|
||||||
C functions are called by pushing their args on the stack, and JSR'ing to the
|
|
||||||
entry point. (See ex 1, below) If the function returns a value, it comes back
|
|
||||||
in AX. NOTE!!! A potentially significant difference between the CC65
|
|
||||||
environment and other C environments is that the CALLEE pops arguments, not
|
|
||||||
the CALLER. (This is done so as to generate more compact code) In normal use,
|
|
||||||
this doesn't cause any problems, as the normal function entry/exit conventions
|
|
||||||
take care of popping the right number of things off the stack, but you may
|
|
||||||
have to worry about it when doing things like writing hand-coded assembly
|
|
||||||
language routines that take variable numbers of arguments. More about that
|
|
||||||
later.
|
|
||||||
|
|
||||||
Ex 1: Function call: Assuming 'i' declared int and 'c' declared
|
|
||||||
char, the following C code
|
|
||||||
|
|
||||||
i = baz(i, c);
|
|
||||||
|
|
||||||
in absence of a prototype generates this assembler code. I've added
|
|
||||||
the comments.
|
|
||||||
|
|
||||||
lda _i ; get 'i', low byte
|
|
||||||
ldx _i+1 ; get 'i', hi byte
|
|
||||||
jsr pushax ; push it
|
|
||||||
lda _c ; get 'c'
|
|
||||||
ldx #0 ; fill hi byte with 0
|
|
||||||
jsr pushax ; push it
|
|
||||||
ldy #4 ; arg size
|
|
||||||
jsr _baz ; call the function
|
|
||||||
sta _i ; store the result
|
|
||||||
stx _i+1
|
|
||||||
|
|
||||||
In presence of a prototype, the picture changes slightly, since the
|
|
||||||
compiler is able to do some optimizations:
|
|
||||||
|
|
||||||
lda _i ; get 'i', low byte
|
|
||||||
ldx _i+1 ; get 'i', hi byte
|
|
||||||
jsr pushax ; push it
|
|
||||||
lda _c ; get 'c'
|
|
||||||
jsr pusha ; push it
|
|
||||||
jsr _baz ; call the function
|
|
||||||
sta _i ; store the result
|
|
||||||
stx _i+1
|
|
||||||
|
|
||||||
|
|
||||||
Note that the two words of arguments to baz were popped before it exitted.
|
|
||||||
The way baz could tell how much to pop was by the argument count in Y at call
|
|
||||||
time. Thus, even if baz had been called with 3 args instead of the 2 it was
|
|
||||||
expecting, that would not cause stack corruption.
|
|
||||||
|
|
||||||
There's another tricky part about all this, though. Note that the args to baz
|
|
||||||
are pushed in FORWARD order, ie the order they appear in the C statement.
|
|
||||||
That means that if you call a function with a different number of args than it
|
|
||||||
was expecting, they wont end up in the right places, ie if you call baz, as
|
|
||||||
above, with 3 args, it'll operate on the LAST two, not the first two.
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
Symbols:
|
|
||||||
--------
|
|
||||||
|
|
||||||
CC65 does the usual trick of prepending an underbar ('_') to symbol names when
|
|
||||||
compiling them into assembler. Therefore if you have a C function named
|
|
||||||
'bar', CC65 will define and refer to it as '_bar'.
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
Systems:
|
|
||||||
--------
|
|
||||||
|
|
||||||
Supported systems at this time are: C64, C128, Plus/4, CBM 500, CBM 600/700,
|
|
||||||
the newer PET machines (not 2001), Atari 8bit, and the Apple ][ (thanks to
|
|
||||||
Kevin Ruland, who did the port).
|
|
||||||
|
|
||||||
C16: Works with unexpanded or memory expanded C16 and C116 machines.
|
|
||||||
However, a maximum of 32KB from the total memory is used. The Plus/4
|
|
||||||
target supports up to 64K of memory, but has a small code overhead
|
|
||||||
because of the banking routines involved. Apart from this additional
|
|
||||||
overhead, the Plus/4 target and the C16 target are the same. 16K
|
|
||||||
machines (unexpanded C16) have 12K of memory for C programs available,
|
|
||||||
machines with 32K or more have 28K available. The actual amount of
|
|
||||||
memory is auto detected.
|
|
||||||
|
|
||||||
C64: The program runs in a memory configuration, where only the kernal ROM
|
|
||||||
is enabled. The text screen is expected at the usual place ($400), so
|
|
||||||
50K of memory are available to the program.
|
|
||||||
|
|
||||||
C128: The startup code will reprogram the MMU, so that only the kernal ROM
|
|
||||||
is enabled. This means, there are 41K of memory available to the
|
|
||||||
program.
|
|
||||||
|
|
||||||
Plus/4: Works with bank switching so 59K of memory are available to the
|
|
||||||
program.
|
|
||||||
|
|
||||||
CBM 500:
|
|
||||||
The C program runs in bank #0 and has a total of 48K memory available.
|
|
||||||
This is less than what is available on its bigger brothers (CBM
|
|
||||||
600/700) because the character data and video RAM is placed in the
|
|
||||||
execution bank (#0) to allow the use of sprites.
|
|
||||||
|
|
||||||
CBM 600/700:
|
|
||||||
The C program runs in a separate segment and has almost full 64K of
|
|
||||||
memory available.
|
|
||||||
|
|
||||||
PET: The startup code will adjust the upper memory limit to the installed
|
|
||||||
memory. However, only linear memory is used, this limits the top to
|
|
||||||
$8000, so on a 8032 or similar machine, 31K of memory are available to
|
|
||||||
the program.
|
|
||||||
|
|
||||||
Apple ][:
|
|
||||||
The program starts at $803, end of RAM is $95FF, so 35.5K of memory
|
|
||||||
(including stack) are available to the program.
|
|
||||||
|
|
||||||
Atari: The startup code will adjust the upper memory limit to the installed
|
|
||||||
memory detected at runtime. The programmer can adjust the upper memory
|
|
||||||
limit by setting the __RESERVED_MEMORY__ variable at link time. The
|
|
||||||
given __RESERVED_MEMORY__ value will be subtracted from the upper
|
|
||||||
memory limit used by the runtine. This memory could be used as graphics
|
|
||||||
memory, for example.
|
|
||||||
In the default case (no setting of __RESERVED_MEMORY__) the upper
|
|
||||||
memory limit is $9C1F (with Basic cartridge) and $BC1F (without
|
|
||||||
cartridge). The program starts at $2E00 by default.
|
|
||||||
These values are for a 48K or 64K machine.
|
|
||||||
|
|
||||||
Note: The above numbers do not mean that the remaining memory is unusable.
|
|
||||||
However, it is not linear memory and must be accessed by other, nonportable
|
|
||||||
methods. I'm thinking about a library extension that allows access to the
|
|
||||||
additional memory as a far heap, but these routines do not exist until now.
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
Inline Assembly:
|
|
||||||
----------------
|
|
||||||
|
|
||||||
CC65 allows inline assembly by a special keyword named "asm". Inline assembly
|
|
||||||
looks like a function call. The string in parenthesis is output in the
|
|
||||||
assembler file.
|
|
||||||
|
|
||||||
Example, insert a break instruction into the code:
|
|
||||||
|
|
||||||
asm ("brk")
|
|
||||||
|
|
||||||
Beware: Be careful when inserting inline code since this may collide with
|
|
||||||
the work of the optimizer.
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
Pseudo variables:
|
|
||||||
-----------------
|
|
||||||
|
|
||||||
There are two special variables available named __AX__ and __EAX__. These
|
|
||||||
variables must never be declared (this gives an error), but may be used as any
|
|
||||||
other variable. However, accessing these variables will access the primary
|
|
||||||
register that is used by the compiler to evaluate expressions, return
|
|
||||||
functions results and pass parameters.
|
|
||||||
|
|
||||||
This feature is useful with inline assembly and macros. For example, a macro
|
|
||||||
that reads a CRTC register may be written like this:
|
|
||||||
|
|
||||||
#define wr(idx) (__AX__=(idx), \
|
|
||||||
asm ("sta $2000"), \
|
|
||||||
asm ("lda $2000"), \
|
|
||||||
asm ("ldx #$00"), \
|
|
||||||
__AX__)
|
|
||||||
|
|
Loading…
Add table
Reference in a new issue