A library that realizes a copper-like functionality of firing certain predefined handlers at programmable raster lines. This library utilizes raster interrupt functionality of VIC-II.
The concept of writing libraries for KickAssembler has been already roughly sketched in my blog post. Copper64 is intended to be a library too. Copper64 requires other c64lib libraries such as common, chipset and text.
As for now there are three example programs available that demonstrate capabilities of Copper64 library (all are placed in examples
directory):
e01-color-raster-bars.asm
- shows colorful raster bars using different combination of border, background and both while playing Noisy Pillars of Jeroen Tel in the background.e02-screen-modes.asm
- shows several different screen modes mixed together.e03-bitmap-demo.asm
- mixes regular text and hires bitmap modes while playing music and animating background.
For more sophisticated example please refer my bluevessel intro.
As library management system is not yet complete, you have to do several steps manually in order to be able to assembly and run examples. One possible approach is following:
-
Create new directory and name it i.e.:
c64lib
-
Inside of this directory clone
common
,chipset
,text
andcopper64
libraries:git clone https://github.com/c64lib/common.git
git clone https://github.com/c64lib/chipset.git
git clone https://github.com/c64lib/text.git
git clone https://github.com/c64lib/copper64.git
-
Assuming that your KickAssembler JAR file is located under
c:\cbm\KickAss.jar
and yourc64lib
directory is located underc:\cbm\c64lib
run assembler inside ofexamples
directory:java -jar c:\cbm\KickAss.jar -libdir c:\cbm\c64lib e01-color-raster-bars.asm
java -jar c:\cbm\KickAss.jar -libdir c:\cbm\c64lib e02-screen-modes.asm
java -jar c:\cbm\KickAss.jar -libdir c:\cbm\c64lib e03-bitmap-demo.asm
-
In result you should get three PRG files that can be launched using VICE or transferred to real hardware and launched there.
Easiest way to learn how to create own "copper list" is to study one of examples that are provided in copper64 repository. Copper list is a data structure that should fit entirely into single page of memory (256b) - to ensure this always use .align $100
directive prior list declaration. Definition of copper list is simplified due to two KickAssembler macros: copperEntry
and copperLoop
.
copperEntry
can be used multiple times to "install" one of few available IRQ handlers at given raster position. copperLoop
must always be a last position in display list and it informs copper64 that the list is over and it should loop to the beginning.
Let's look at following example:
.align $100
copperList: {
copperEntry(46, c64lib.IRQH_BORDER_COL, WHITE, 0)
copperEntry(81, c64lib.IRQH_BG_COL_0, YELLOW, 0)
copperEntry(101, c64lib.IRQH_BG_COL_0, LIGHT_GREEN, 0)
copperEntry(124, c64lib.IRQH_BG_COL_0, GREY, 0)
copperEntry(131, c64lib.IRQH_BG_COL_0, BLUE, 0)
copperEntry(150, c64lib.IRQH_BORDER_COL, RED, 0)
copperEntry(216, c64lib.IRQH_BORDER_BG_0_COL, LIGHT_GREY, $00)
copperEntry(221, c64lib.IRQH_BORDER_BG_0_COL, GREY, $00)
copperEntry(227, c64lib.IRQH_BORDER_BG_0_COL, DARK_GREY, $00)
copperEntry(232, c64lib.IRQH_BORDER_BG_0_DIFF, RED, BLUE)
copperEntry(252, c64lib.IRQH_BORDER_COL, LIGHT_BLUE, 0)
copperEntry(257, c64lib.IRQH_JSR, <custom1, >custom1)
copperLoop()
}
We mark beginning of the list with label (copperList
), because we need this address later on when initializing copper64.
Then we have sequence of copper entries, each taking couple of parameters. First parameter is always a raster line (we can use here numbers greater than 255, macro takes care of handling this extra bit). It is up to coder to ensure that these numbers are ordered and growing. If you mess up ordering, you'll get junk on the screen - you have been warned.
Copper64 uses double interrupt technique to stabilize raster. Therefore you cannot install handlers too often - there will be not enough time to launch next handler just because of this stabilization that can take up to 3 raster lines. You also need to remember, that stabilization does not support active sprites (yet) nor bad lines. So, you should place your entries wisely.
Second attribute to the entry is always a handler code. All supported handlers are defined as KickAssembler labels in copper64 library (all of them start with IRQH_
prefix). There is one limitation: overall size of handlers code cannot extend single memory page (256 bytes). In consequence, all existing handlers cannot be used in the same time (they weight too much), therefore there is enabling mechanism available. In your program you must enable each handler that you're going to use manually:
#define IRQH_BORDER_COL
#define IRQH_BG_COL_0
#define IRQH_BORDER_BG_0_COL
#define IRQH_BORDER_BG_0_DIFF
#define IRQH_JSR
#import "chipset/lib/mos6510.asm"
#import "chipset/lib/vic2.asm"
#import "text/lib/text.asm"
#import "copper64/lib/copper64.asm"
As you see you can do it with #define
directive. Just make sure that you define all of them before importing copper64.asm
, otherwise it will not work.
Next two parameters have different meaning depending on the handler.
Examples:
copperEntry(46, c64lib.IRQH_BORDER_COL, WHITE, 0)
Changes border color to WHITE in line 46. In this case last parameter is not used, we set it to 0.
copperEntry(232, c64lib.IRQH_BORDER_BG_0_DIFF, RED, BLUE)
Changes border color to RED and background color to BLUE in line 232.
copperEntry(257, c64lib.IRQH_JSR, <custom1, >custom1)
Launches custom subroutine at line 257, address of this subroutine is specified in last two parameters.
The initCopper
macro installs copper64 initialization routine that can be then called with jsr. Macro takes two parameters - an arbitrary chosen addresses on zero page. That's it - copper64 requires only two zero page registers for functioning. It's up to you which two to choose.
// set up address of display list
lda #<copperList
sta DISPLAY_LIST_PTR_LO
lda #>copperList
sta DISPLAY_LIST_PTR_HI
// initialize copper64 routine
jsr copper
block:
nop
lda $ff00
lda $ff00,y
jmp block // infinite loop
copper: {
initCopper(DISPLAY_LIST_PTR_LO, LIST_PTR)
}
Changes border color.
- Handler label:
IRQH_BORDER_COL
- Handler code:
1
- Argument 1: desired border color;
0..15
- Argument 2: unused
- Cycled: yes (PAL, 63 cycles)
Usage:
copperEntry(<raster>, c64lib.IRQH_BORDER_COL, <color>, 0)
Changes background color 0.
- Handler label:
IRQH_BG_COL_0
- Handler code:
2
- Argument 1: desired background 0 color;
0..15
- Argument 2: unused
- Cycled: yes (PAL, 63 cycles)
Usage:
copperEntry(<raster>, c64lib.IRQH_BG_COL_0, <color>, 0)
Changes background color 1.
- Handler label:
IRQH_BG_COL_1
- Handler code:
3
- Argument 1: desired background 1 color;
0..15
- Argument 2: unused
- Cycled: yes (PAL, 63 cycles)
Usage:
copperEntry(<raster>, c64lib.IRQH_BG_COL_1, <color>, 0)
Changes background color 2.
- Handler label:
IRQH_BG_COL_2
- Handler code:
4
- Argument 1: desired background 2 color;
0..15
- Argument 2: unused
- Cycled: yes (PAL, 63 cycles)
Usage:
copperEntry(<raster>, c64lib.IRQH_BG_COL_2, <color>, 0)
Changes background color 3.
- Handler label:
IRQH_BG_COL_3
- Handler code:
5
- Argument 1: desired background 3 color;
0..15
- Argument 2: unused
- Cycled: yes (PAL, 63 cycles)
Usage:
copperEntry(<raster>, c64lib.IRQH_BG_COL_3, <color>, 0)
Changes background color 0 and border color to the same color.
- Handler label:
IRQH_BORDER_BG_0_COL
- Handler code:
6
- Argument 1: desired color for border and background 0;
0..15
- Argument 2: unused
- Cycled: yes (PAL, 63 cycles)
Usage:
copperEntry(<raster>, c64lib.IRQH_BORDER_BG_0_COL, <color>, 0)
Changes background color 0 and border color to another values in single step, the colors are specified as arguments.
- Handler label:
IRQH_BORDER_BG_0_DIFF
- Handler code:
7
- Argument 1: desired color for border;
0..15
- Argument 2: desired color for background 0;
0..15
- Cycled: yes (PAL, 63 cycles)
Usage:
copperEntry(<raster>, c64lib.IRQH_BORDER_BG_0_DIFF, <border color>, <background color>)
Changes VIC memory control and VIC memory bank in one step.
- Handler label:
IRQH_MEM_BANK
- Handler code:
8
- Argument 1: value for
MEMORY_CONTROL
register - Argument 2: value for VIC bank (goes to
CIA2_DATA_PORT_A
); only two least significant bits are taken, other bits of the data port are preserved - Cycled: yes (PAL, 63 cycles)
Usage:
copperEntry(<raster>, c64lib.IRQH_MEM_BANK, <memory control>, <vic bank number>)
Changes VIC display mode and memory settings in one step. VIC bank cannot be changed.
- Handler label:
IRQH_MODE_MEM
- Handler code:
9
- Argument 1: mode of vic2; for performance reasons the values for two control registers are packed in one byte:
%00010000
for Multicolor,%01100000
for ECM or Bitmap - Argument 2: value for
MEMORY_CONTROL
register - Cycled: yes (PAL, 63 cycles)
Usage:
copperEntry(<raster>, c64lib.IRQH_MODE_MEM, <vic mode>, <memory control>)
Jumps to custom subroutine that can do whatever you want, i.e. play music. Subroutine must end with rts
.
- Handler label:
IRQH_JSR
- Handler code:
10
- Argument 1: Low byte of subroutine address
- Argument 2: High byte of subroutine address
- Cycled: no
Usage:
copperEntry(<raster>, c64lib.IRQH_JSR, <address, >address)
Sets up hires bitmap mode using given memory layout and VIC bank. Useful for screen splits using totally different memory locations for VIC chip.
- Handler label:
IRQH_MODE_HIRES_BITMAP
- Handler code:
11
- Argument 1: value for
MEMORY_CONTROL
register - Argument 2: value for VIC bank (goes to
CIA2_DATA_PORT_A
); only two least significant bits are taken, other bits of the data port are preserved - Cycled: yes (PAL, 63 cycles)
Usage:
copperEntry(<raster>, c64lib.IRQH_MODE_HIRES_BITMAP, <memory control>, <vic bank number>)
Sets up multicolor bitmap mode using given memory layout and VIC bank. Useful for screen splits using totally different memory locations for VIC chip.
- Handler label:
IRQH_MODE_MULTIC_BITMAP
- Handler code:
12
- Argument 1: value for
MEMORY_CONTROL
register - Argument 2: value for VIC bank (goes to
CIA2_DATA_PORT_A
); only two least significant bits are taken, other bits of the data port are preserved - Cycled: yes (PAL, 63 cycles)
Usage:
copperEntry(<raster>, c64lib.IRQH_MODE_MULTIC_BITMAP, <memory control>, <vic bank number>)
Sets up hires text mode using given memory layout and VIC bank. Useful for screen splits using totally different memory locations for VIC chip.
- Handler label:
IRQH_MODE_HIRES_TEXT
- Handler code:
13
- Argument 1: value for
MEMORY_CONTROL
register - Argument 2: value for VIC bank (goes to
CIA2_DATA_PORT_A
); only two least significant bits are taken, other bits of the data port are preserved - Cycled: yes (PAL, 63 cycles)
Usage:
copperEntry(<raster>, c64lib.IRQH_MODE_HIRES_TEXT, <memory control>, <vic bank number>)
Sets up multicolor text mode using given memory layout and VIC bank. Useful for screen splits using totally different memory locations for VIC chip.
- Handler label:
IRQH_MODE_MULTIC_TEXT
- Handler code:
14
- Argument 1: value for
MEMORY_CONTROL
register - Argument 2: value for VIC bank (goes to
CIA2_DATA_PORT_A
); only two least significant bits are taken, other bits of the data port are preserved - Cycled: yes (PAL, 63 cycles)
Usage:
copperEntry(<raster>, c64lib.IRQH_MODE_MULTIC_TEXT, <memory control>, <vic bank number>)
Sets up extended text mode using given memory layout and VIC bank. Useful for screen splits using totally different memory locations for VIC chip.
- Handler label:
IRQH_MODE_EXTENDED_TEXT
- Handler code:
15
- Argument 1: value for
MEMORY_CONTROL
register - Argument 2: value for VIC bank (goes to
CIA2_DATA_PORT_A
); only two least significant bits are taken, other bits of the data port are preserved - Cycled: yes (PAL, 63 cycles)
Usage:
copperEntry(<raster>, c64lib.IRQH_MODE_EXTENDED_TEXT, <memory control>, <vic bank number>)
Generates colorful raster bar across whole screen including border. Color for each subsequent bar line is fetched from $FF
terminated array of colors (values 0..15
). Because procedure is cycled using busy waiting on raster, a raster time for whole bar will be consumed. Color array can be cycled or modified in any way to get interesting animation effects.
- Handler label:
IRQH_FULL_RASTER_BAR
- Handler code:
16
- Argument 1: Low byte of bar color definition address
- Argument 2: High byte of bar color definition address
- Cycled: yes (PAL, 63 cycles) - it sucks on badlines however
Usage:
copperEntry(<raster>, c64lib.IRQH_FULL_RASTER_BAR, <address, >address)
Generates colorful raster bar across whole background. Color for each subsequent bar line is fetched from $FF
terminated array of colors (values 0..15
). Because procedure is cycled using busy waiting on raster, a raster time for whole bar will be consumed. Color array can be cycled or modified in any way to get interesting animation effects.
- Handler label:
IRQH_BG_RASTER_BAR
- Handler code:
17
- Argument 1: Low byte of bar color definition address
- Argument 2: High byte of bar color definition address
- Cycled: yes (PAL, 63 cycles)
Usage:
copperEntry(<raster>, c64lib.IRQH_BG_RASTER_BAR, <colorCycleDef, >colorCycleDef)
...
colorCycleDef: .byte COLOR_3, LIGHT_RED, RED, LIGHT_RED, YELLOW, WHITE, YELLOW, YELLOW, COLOR_3, $ff
Scrolls screen horizontally using specified amount of pixels.
- Handler label:
IRQH_HSCROLL
- Handler code:
17
- Argument 1: value for horizontal scroll register (
0..7
) - Argument 2: unused
- Cycled: yes (PAL, 63 cycles)
Usage:
copperEntry(<raster>, c64lib.IRQH_HSCROLL, <scroll value>, 0)
Applies shallow tech-tech effect (using values 0..7
) starting from given raster position. Horizontal scroll value for each corresponding raster line is taken from $FF
terminated array of values, each should contain value from 0..7
range. The scroll map can be further modified (i.e. rotated) to achieve interesting animation effects.
- Handler label:
IRQH_HSCROLL_MAP
- Handler code:
17
- Argument 1: low byte of scroll map definition address
- Argument 2: high value of scroll map definition address
- Cycled: yes (PAL, 63 cycles)
Usage:
copperEntry(<raster>, c64lib.IRQH_HSCROLL_MAP, <hscrollMapDef, >hscrollMapDef)
...
hscrollMapDef: .fill TECH_TECH_WIDTH, round(3.5 + 3.5*sin(toRadians(i*360/TECH_TECH_WIDTH))) ; .byte 0; .byte $ff
Copper64 operates on IRQ table consisting of IRQ entries. Each IRQ entry is a following structure of 4 bytes:
Offset | Code | Description |
---|---|---|
0 | CTRL | Control byte; specifies handler id and some additional data. |
1 | RAS | Raster counter; low 8 bits of raster counter. 9-th bit is stored in CTRL. |
2 | DAT1 | Data byte 1 |
3 | DAT2 | Data byte 2 |
Control byte (CTRL) has following meaning:
$00 | stop |
$FF | loop |
b7 | raster 8 |
b6 | reserved |
b5 | reserved |
b4..0 | function - 1..31 |
- New handler:
IRQH_DASHBOARD_CUTOFF
- New handler:
IRQH_VSCROLL
- New handler:
IRQH_VSCROLL_NTSC
- Prestabilization in BG raster bar (helps with NTSC stability).
- Bug fix:
stopCopper
macro was not working.
- Public symbols defined as global in
-global.asm
file.