mirror of
https://github.com/irmen/prog8.git
synced 2024-12-26 14:29:35 +00:00
169 lines
6.9 KiB
ReStructuredText
169 lines
6.9 KiB
ReStructuredText
***************************
|
|
Target system specification
|
|
***************************
|
|
|
|
Prog8 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 Prog8 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`` internal scratch variable
|
|
``$03`` internal scratch variable
|
|
``$fb - $fc`` internal scratch variable
|
|
``$fd - $fe`` internal 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``.
|
|
|
|
Prog8 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 the compiler allocate these addresses for you and just
|
|
use symbolic names in the program code.
|
|
|
|
Prog8 knows what addresses are safe to use in the various ZP handling configurations.
|
|
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.
|
|
|
|
**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 reasonably restrictive to use the
|
|
part of the ZP that is not used by the C64's kernal routines.
|
|
It's possible to claim the whole ZP as well (by disabling the operating system or kernal).
|
|
If you want, it's also possible to be more restricive and stay clear of the addresses used by BASIC routines too.
|
|
This allows the program to exit cleanly back to a BASIC ready prompt - something that is not possible in the other modes.
|
|
|
|
|
|
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!
|
|
|
|
|
|
|
|
|
|
CPU
|
|
===
|
|
|
|
Directly Usable Registers
|
|
-------------------------
|
|
|
|
The hardware CPU registers are not directly accessible from regular Prog8 code.
|
|
If you need to mess with them, you'll have to use inline assembly.
|
|
Be extra wary of the ``X`` register because it is used as an evaluation stack pointer and
|
|
changing its value you will destroy the evaluation stack and likely crash the program.
|
|
|
|
The status register (P) carry flag and interrupt disable flag can be written via a couple of special
|
|
builtin functions (``set_carry()``, ``clear_carry()``, ``set_irqd()``, ``clear_irqd()``),
|
|
and read via the ``read_flags()`` function.
|
|
|
|
|
|
Subroutine Calling Conventions
|
|
------------------------------
|
|
|
|
**Kernel/assembly subroutines:**
|
|
Arguments and results are passed via registers.
|
|
Sometimes the status register's Carry flag is used as well (as a boolean flag).
|
|
Special care should be taken when the subroutine clobbers the X register.
|
|
If it does, X must be saved before and restored after the call.
|
|
|
|
**Normal user defined subroutines:**
|
|
Arguments and result values are passed via global variables stored in memory
|
|
*These are not allocated on a stack* so it is not possible to create recursive calls!
|
|
The result value(s) of a subroutine are returned on the evaluation stack,
|
|
to make it possible to use subroutines in expressions.
|
|
|
|
|
|
IRQ Handling
|
|
============
|
|
|
|
Normally, the system's default IRQ handling is not interfered with.
|
|
You can however install your own IRQ handler.
|
|
This is possible ofcourse by doing it all using customized inline assembly,
|
|
but there are a few library routines available to make setting up C-64 IRQs and raster IRQs a lot easier (no assembly code required).
|
|
|
|
These routines are::
|
|
|
|
c64utils.set_irqvec()
|
|
c64utils.set_irqvec_excl()
|
|
|
|
c64utils.set_rasterirq( <raster line> )
|
|
c64utils.set_rasterirq_excl( <raster line> )
|
|
|
|
c64utils.restore_irqvec() ; set it back to the systems default irq handler
|
|
|
|
If you activate an IRQ handler with one of these, it expects the handler to be defined
|
|
as a subroutine ``irq`` in the module ``irq`` so like this::
|
|
|
|
irq {
|
|
sub irq() {
|
|
; ... irq handling here ...
|
|
}
|
|
}
|
|
|