mirror of
https://github.com/irmen/prog8.git
synced 2024-09-10 09:54:32 +00:00
230 lines
11 KiB
ReStructuredText
230 lines
11 KiB
ReStructuredText
===========================
|
|
Target system specification
|
|
===========================
|
|
|
|
Prog8 targets the following hardware:
|
|
|
|
- 8 bit MOS 6502/65c02/6510 CPU
|
|
- 64 Kb addressable memory (RAM or ROM)
|
|
- optional use of memory mapped I/O registers
|
|
- optional use of system ROM routines
|
|
|
|
Currently these machines can be selected as a compilation target (via the ``-target`` compiler argument):
|
|
|
|
- 'c64': the Commodore 64
|
|
- 'cx16': the `Commander X16 <https://www.commanderx16.com/>`_
|
|
- 'c128': the Commodore 128 (*limited support*)
|
|
- 'pet32': the Commodore PET 4032 (*limited support*)
|
|
- 'atari': the Atari 800 XL (*experimental support*)
|
|
- 'virtual': a builtin virtual machine
|
|
|
|
This chapter explains some relevant system details of the c64 and cx16 machines.
|
|
|
|
.. hint::
|
|
If you only use standard Kernal and prog8 library routines,
|
|
it is often possible to compile the *exact same program* for
|
|
different machines (just change the compilation target flag)!
|
|
|
|
|
|
Memory Model
|
|
============
|
|
|
|
Generic 6502 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 support for RAM expansions or bank switching built natively into the language.
|
|
|
|
====================== ================== ========
|
|
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
|
|
depending on the specific computer system
|
|
====================== ================== ========
|
|
|
|
|
|
Memory map for the C64 and the X16
|
|
----------------------------------
|
|
|
|
This is the default memory map of the 64 Kb addressable memory for those two systems.
|
|
Both systems have ways to alter the memory map and/or to switch memory banks, but that is not shown here.
|
|
|
|
.. image:: memorymap.svg
|
|
|
|
Footnotes for the Commander X16
|
|
^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
|
|
*Golden Ram $0400 - $07FF*
|
|
*free to use.*
|
|
|
|
*Zero Page $0000 - $00FF*
|
|
$00 and $01 are hardwired as Rom and Ram banking registers.
|
|
|
|
$02 - $21 are the 16 virtual cx16 registers R0-R15.
|
|
|
|
$22 - $7F are used by Prog8 to put variables in.
|
|
|
|
The top half of the ZP ($80-$FF) is reserved for use by the Kernal and Basic in normal operation.
|
|
Zero page use by Prog8 can be manipulated with the ``%zeropage`` directive, various options
|
|
may free up more locations for use by Prog8 or to reserve them for other things.
|
|
|
|
|
|
Footnotes for the Commodore 64
|
|
^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
|
|
|
|
*RAM $C000-$CFFF*
|
|
*free to use:* $C000 - $CFDF
|
|
*reserved:* $CFE0 - $CFFF for the 16 virtual cx16 registers R0-R15
|
|
|
|
*Zero Page $0000 - $00FF*
|
|
Consider the full zero page to be reserved for use by the Kernal and Basic in normal operation.
|
|
Zero page use by Prog8 can be manipulated with the ``%zeropage`` directive, various options
|
|
may free up more locations for use by Prog8 or to reserve them for other things.
|
|
|
|
|
|
Zero page usage by the Prog8 compiler
|
|
-------------------------------------
|
|
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 restrictive 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 C64'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.
|
|
|
|
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.
|
|
|
|
The 16 'virtual' 16-bit registers that are defined on the Commander X16 machine are not real hardware
|
|
registers and are just 16 memory-mapped word values that you *can* access directly.
|
|
|
|
|
|
IRQ Handling
|
|
============
|
|
|
|
Normally, the system's default IRQ handling is not interfered with.
|
|
You can however install your own IRQ handler (for clean separation, it is advised to define it inside its own block).
|
|
There are a few library routines available to make setting up 60hz/vsync IRQs and raster/line IRQs a lot easier (no assembly code required).
|
|
|
|
These routines are::
|
|
|
|
sys.set_irq(uword handler_address)
|
|
sys.set_rasterirq(uword handler_address, uword rasterline)
|
|
sys.restore_irq() ; set everything back to the systems default irq handler
|
|
|
|
The IRQ handler routine must return a boolean value (0 or 1) in the A register:
|
|
0 means do *not* run the system IRQ handler routine afterwards, 1 means run the system IRQ handler routine afterwards.
|
|
|
|
|
|
**CommanderX16 specific notes**
|
|
|
|
Note that for the CommanderX16 the set_rasterirq() will disable VSYNC irqs and never call the system IRQ handler regardless
|
|
of the return value of the user handler routine. This also means the default sys.wait() routine won't work anymore,
|
|
when using this handler.
|
|
|
|
These two helper routines are not particularly suited to handle multiple IRQ sources on the Commander X16.
|
|
It's possible but it requires correct fiddling with IRQ enable bits, acknowledging the IRQs, and properly calling
|
|
or not calling the system IRQ handler routine. See the section below for perhaps a better and easier solution that
|
|
is tailored to this system.
|
|
|
|
The Commander X16 syslib provides some additional routines that should be used *in your IRQ handler routine* if it uses the Vera registers.
|
|
They take care of saving and restoring the Vera state of the interrupted main program, otherwise the IRQ handler's manipulation
|
|
will corrupt any Vera operations that were going on in the main program. The routines are::
|
|
|
|
cx16.save_vera_context()
|
|
; perhaps also cx16.save_virtual_registers() here...
|
|
; ... do your work that uses vera here!...
|
|
; perhaps also cx16.restore_virtual_registers() here...
|
|
cx16.restore_vera_context()
|
|
|
|
.. caution::
|
|
The Commander X16's 16 'virtual registers' R0-R15 are located in zeropage and *are not preserved* in the IRQ handler!
|
|
So you should make sure that the handler routine does NOT use these registers, or do some sort of saving/restoring yourself
|
|
of the ones that you do need in the IRQ handler.
|
|
There are two utility routines in cx16 that save and restore *all* 16 registers so it's a bit inefficient but safe.
|
|
(these are ``save_virtual_registers()`` and ``restore_virtual_registers()``)
|
|
|
|
It is also advised to not use floating point calculations inside IRQ handler routines.
|
|
Beside them being very slow, there are intricate requirements such as having the
|
|
correct ROM bank enabled to be able to successfully call them (and making sure the correct
|
|
ROM bank is reset at the end of the handler), and the possibility
|
|
of corrupting variables and floating point calculations that are being executed
|
|
in the interrupted main program. These memory locations should be backed up
|
|
and restored at the end of the handler, further increasing its execution time...
|
|
|
|
|
|
Commander X16 specific IRQ handling
|
|
===================================
|
|
|
|
Instead of using the routines in ``sys`` as mentioned above (that are more or less portable
|
|
across the C64,C128 and cx16), you can also use the special routines made for the Commander X16,
|
|
in ``cx16``. The idea is to let Prog8 do the irq dispatching and housekeeping for you, and that
|
|
your program only has to register the specific handlers for the specific IRQ sources that you want to handle.
|
|
|
|
Look at the examples/cx16/multi-irq-new.p8 example to see how these routines can be used.
|
|
Here they are, all available in ``cx16``:
|
|
|
|
``disable_irqs ()``
|
|
Disables all Vera IRQ sources. Note that the CPU irq disable flag is not changed by this routine.
|
|
you can manipulate that via ``sys.set_irqd()`` and ``sys.clear_irqd()`` as usual.
|
|
|
|
``enable_irq_handlers (bool disable_all_irq_sources)``
|
|
Install the "master IRQ handler" that will dispatch IRQs to the registered handler for each type.
|
|
Only Vera IRQs supported for now.
|
|
Pass true to initially disable all Vera interrupt sources (they will be enabled individually again
|
|
by setting the various handlers), or pass false to not touch this.
|
|
The handlers don't need to clear its ISR bit, but have to return 0 or 1 in A,
|
|
where 1 means: continue with the system IRQ handler, 0 means: don't call that.
|
|
The order in which the handlers are invoked if multiple interrupts occur simultaneously is: LINE, SPRCOL, AFLOW, VSYNC.
|
|
|
|
``set_vsync_irq_handler (uword address)``
|
|
Sets the verical sync interrupt handler routine. Also enables VSYNC interrupts.
|
|
|
|
``set_line_irq_handler (uword rasterline, uword address)``
|
|
Sets the rasterline interrupt handler routine to trigger on the specified raster line.
|
|
Also enables LINE interrupts.
|
|
You can use ``sys.set_rasterline()`` later to adjust the rasterline on which to trigger.
|
|
|
|
``set_sprcol_irq_handler (uword address)``
|
|
Sets the sprite collision interrupt handler routine. Also enables SPRCOL interrupts.
|
|
|
|
``set_aflow_irq_handler (uword address)``
|
|
Sets the audio buffer underrun interrupt handler routine. Also enables AFLOW interrupts.
|
|
|
|
``disable_irq_handlers ()``
|
|
Hand control back to the system default IRQ handler.
|