mirror of
https://github.com/irmen/prog8.git
synced 2024-12-25 23:29:55 +00:00
168 lines
7.5 KiB
ReStructuredText
168 lines
7.5 KiB
ReStructuredText
|
***************************
|
||
|
Target system specification
|
||
|
***************************
|
||
|
|
||
|
IL65 targets the following hardware:
|
||
|
|
||
|
- 8 bit MOS 6502/6510 CPU
|
||
|
- 64 Kb addressable memory (RAM or ROM)
|
||
|
- memory mapped I/O registers
|
||
|
|
||
|
The main target machine is the Commodore-64, which is an example of this.
|
||
|
This chapter explains the relevant system details of such a machine.
|
||
|
|
||
|
|
||
|
Memory Model
|
||
|
============
|
||
|
|
||
|
Physical address space layout
|
||
|
-----------------------------
|
||
|
|
||
|
The 6502 CPU can address 64 kilobyte of memory.
|
||
|
Most of the 64 kilobyte address space can be used by IL65 programs.
|
||
|
This is a hard limit: there is no built-in support for RAM expansions or bank switching.
|
||
|
|
||
|
|
||
|
====================== ================== ========
|
||
|
memory area type note
|
||
|
====================== ================== ========
|
||
|
``$00``--``$ff`` ZeroPage contains many sensitive system variables
|
||
|
``$100``--``$1ff`` Hardware stack used by the CPU, normally not accessed directly
|
||
|
``$0200``--``$ffff`` Free RAM or ROM free to use memory area, often a mix of RAM and ROM
|
||
|
====================== ================== ========
|
||
|
|
||
|
|
||
|
A few of these memory addresses are reserved and cannot be used for arbitrary data.
|
||
|
They have a special hardware function, or are reserved for internal use in the
|
||
|
code generated by the compiler:
|
||
|
|
||
|
================== =======================
|
||
|
reserved address in use for
|
||
|
================== =======================
|
||
|
``$00`` data direction (CPU hw)
|
||
|
``$01`` bank select (CPU hw)
|
||
|
``$02`` IL65 scratch variable
|
||
|
``$03`` IL65 scratch variable
|
||
|
``$fb - $fc`` IL65 scratch variable
|
||
|
``$fd - $fe`` IL65 scratch variable
|
||
|
``$fffa - $fffb`` NMI vector (CPU hw)
|
||
|
``$fffc - $fffd`` RESET vector (CPU hw)
|
||
|
``$fffe - $ffff`` IRQ vector (CPU hw)
|
||
|
================== =======================
|
||
|
|
||
|
The actual machine will often have many other special addresses as well,
|
||
|
For example, the Commodore-64 has:
|
||
|
|
||
|
- ROMs installed in the machine: BASIC, kernal and character roms. Occupying ``$a000``--``$bfff`` and ``$e000``--``$ffff``.
|
||
|
- memory-mapped I/O registers, for the video and sound chips, and the CIA's. Occupying ``$d000``--``$dfff``.
|
||
|
- RAM areas that are used for screen graphics and sprite data: usually at ``$0400``--``$07ff``.
|
||
|
|
||
|
IL65 programs can access all of those special memory locations but it will have a special meaning.
|
||
|
|
||
|
|
||
|
.. _zeropage:
|
||
|
|
||
|
ZeroPage ("ZP")
|
||
|
---------------
|
||
|
|
||
|
The ZeroPage memory block ``$02``--``$ff`` can be regarded as 254 CPU 'registers', because
|
||
|
they take less clock cycles to access and need fewer instruction bytes than accessing other memory locations outside of the ZP.
|
||
|
Theoretically they can all be used in a program, with the follwoing limitations:
|
||
|
|
||
|
- several addresses (``$02``, ``$03``, ``$fb - $fc``, ``$fd - $fe``) are reserved for internal use
|
||
|
- most other addresses will already be in use by the machine's operating system or kernal,
|
||
|
and overwriting them will probably crash the machine. It is possible to use all of these
|
||
|
yourself, but only if the program takes over the entire system (and seizes control from the regular kernal).
|
||
|
This means it can no longer use (most) BASIC and kernal routines from ROM.
|
||
|
- it's more convenient and safe to let IL65 allocate these addresses for you and just
|
||
|
use symbolic names in the program code.
|
||
|
|
||
|
Here is the list of the remaining free-to-use ZP addresses with BASIC and KERNAL active in the Commodore-64:
|
||
|
|
||
|
``$02``, ``$03``, ``$04``, ``$05``, ``$06``, ``$2a``, ``$52``,
|
||
|
``$f7 - $f8``, ``$f9 - $fa``, ``$fb - $fc``, ``$fd - $fe``
|
||
|
|
||
|
*The six reserved addresses mentioned earliser are subtracted from this set,* leaving you with
|
||
|
just *five* 1-byte and *two* 2-byte usable ZP 'registers' for use by the program.
|
||
|
|
||
|
**IL65 knows about all of this.** It will use the free ZP addresses to place its ZP variables in,
|
||
|
until they're all used up. If instructed to output a program that takes over the entire
|
||
|
machine, (almost) all of the ZP addresses are suddenly available and will be used.
|
||
|
IL65 can also generate a special routine that saves and restores the ZP to let the program run
|
||
|
and return safely back to the system afterwards - you don't have to take care of that yourself.
|
||
|
|
||
|
|
||
|
IRQs and the ZeroPage
|
||
|
^^^^^^^^^^^^^^^^^^^^^
|
||
|
|
||
|
The normal IRQ routine in the C-64's kernal will read and write several addresses in the ZP
|
||
|
(such as the system's software jiffy clock which sits in ``$a0 - $a2``):
|
||
|
|
||
|
``$a0 - $a2``; ``$91``; ``$c0``; ``$c5``; ``$cb``; ``$f5 - $f6``
|
||
|
|
||
|
These addresses will never be used by the compiler for ZP variables, so variables will
|
||
|
not interfere with the IRQ routine and vice versa. This is true for the normal ZP mode but also
|
||
|
for the mode where the whole system and ZP have been taken over.
|
||
|
So the normal IRQ vector can still run and will be when the program is started!
|
||
|
|
||
|
|
||
|
ZeroPage handling is configurable
|
||
|
^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
|
||
|
|
||
|
There's a global program directive to specify the way the compiler
|
||
|
treats the ZP for the program. The default is to be restrictive to just
|
||
|
the few free locations mentioned above, where most of the ZP is considered a no-go zone by the compiler.
|
||
|
It's possible to claim the whole ZP as well (by disabling the operating system or kernal),
|
||
|
and even ask for a save/restore of the original values to be able to cleanly exit back to a BASIC prompt.
|
||
|
|
||
|
|
||
|
|
||
|
CPU
|
||
|
===
|
||
|
|
||
|
Directly Usable Registers
|
||
|
-------------------------
|
||
|
|
||
|
The following 6502 CPU hardware registers are directly usable in program code (and are reserved symbols):
|
||
|
|
||
|
- ``A``, ``X``, ``Y`` the three main cpu registers (8 bits)
|
||
|
- ``AX``, ``AY``, ``XY`` surrogate 16-bit registers: LSB-order (lo/hi) combined register pairs
|
||
|
- ``SC`` status register's Carry flag
|
||
|
- ``SI`` status register's Interrupt Disable flag
|
||
|
|
||
|
The other status bits of the status register are not directly accessible,
|
||
|
but can be acted upon via conditional statements.
|
||
|
The stack pointer and program counter registers are not accessible.
|
||
|
|
||
|
|
||
|
Subroutine Calling Conventions
|
||
|
------------------------------
|
||
|
|
||
|
Subroutine arguments and results are passed via registers.
|
||
|
Sometimes the status register's Carry flag is used as well (as a boolean flag).
|
||
|
Additional arguments can be passed via memory locations as well ofcourse.
|
||
|
But you'll have to be careful when dealing with chained or even recursive calls then,
|
||
|
because there's a big risk of overwriting those memory locations.
|
||
|
|
||
|
In IL65 the "caller saves" principle applies to calling subroutines.
|
||
|
This means the code that calls a subroutine that clobbers certain
|
||
|
registers (``A``, ``X`` or ``Y``), is responsible for storing and restoring the original values if
|
||
|
those values are needed by the rest of the code.
|
||
|
|
||
|
Normally, registers are *not* preserved when calling a subroutine or when a certian
|
||
|
operations are performed. Most calls will be simply a few instructions to load the
|
||
|
values in the registers and then a ``JSR`` or ``JMP``.
|
||
|
|
||
|
By using the ``%saveregisters`` directive in a block, you can tell the
|
||
|
compiler to preserve all registers. This does generate a lot of extra code that puts
|
||
|
original values on the stack and gets them off the stack again once the subroutine is done.
|
||
|
In this case however you don't have to worry about ``A``, ``X`` and ``Y`` losing their original values
|
||
|
and you can essentially treat them as three local variables instead of scratch data.
|
||
|
|
||
|
You can also use a ``!`` on a single subroutine call to preserve register values, instead of
|
||
|
setting this behavior for the entire block.
|
||
|
|
||
|
.. important::
|
||
|
Basically, you should assume that the 3 hardware registers ``A``, ``X`` and ``Y``
|
||
|
are volatile. Their values cannot be depended upon, unless you explicitly make sure otherwise.
|