mirror of
https://gitlab.com/camelot/kickc.git
synced 2024-11-25 20:32:25 +00:00
Added new_30_years_low_resolution.
This commit is contained in:
parent
a6554c3d37
commit
1fd5cc001c
18
src/test/kc/complex/new_30_years_low_resolution/byteboozer.c
Normal file
18
src/test/kc/complex/new_30_years_low_resolution/byteboozer.c
Normal file
@ -0,0 +1,18 @@
|
||||
// ByteBoozer decruncher
|
||||
// https://github.com/p-a/kickass-cruncher-plugins
|
||||
|
||||
// Decrunch crunched data using ByteBoozer
|
||||
// - crunched: Pointer to the start of the crunched data
|
||||
void byteboozer_decrunch(char* crunched) {
|
||||
asm {
|
||||
ldy crunched
|
||||
ldx crunched+1
|
||||
jsr b2.Decrunch
|
||||
}
|
||||
}
|
||||
|
||||
// The byteboozer decruncher
|
||||
export char BYTEBOOZER[] = kickasm(resource "byteboozer_decrunch.asm") {{
|
||||
.const B2_ZP_BASE = $fc
|
||||
#import "byteboozer_decrunch.asm"
|
||||
}};
|
@ -0,0 +1,6 @@
|
||||
// ByteBoozer decruncher
|
||||
// https://github.com/p-a/kickass-cruncher-plugins
|
||||
|
||||
// Decrunch crunched data using ByteBoozer
|
||||
// - crunched: Pointer to the start of the crunched data
|
||||
void byteboozer_decrunch(char* crunched);
|
@ -0,0 +1,182 @@
|
||||
// ByteBoozer Decruncher /HCL May.2003
|
||||
// B2 Decruncher December 2014
|
||||
|
||||
.importonce
|
||||
.filenamespace b2
|
||||
|
||||
// You must set .const B2_ZP_BASE prior the import of this file
|
||||
.if (B2_ZP_BASE > $ff) {
|
||||
.error "B2_ZP_BASE must be in zeropage. Was $" + toHexString(B2_ZP_BASE,4)
|
||||
}
|
||||
|
||||
|
||||
.label zp_base = B2_ZP_BASE
|
||||
.label bits = zp_base
|
||||
.label put = zp_base + 2
|
||||
|
||||
.macro @B2_DECRUNCH(addr) {
|
||||
ldy #<addr
|
||||
ldx #>addr
|
||||
jsr b2.Decrunch
|
||||
}
|
||||
|
||||
.macro GetNextBit() {
|
||||
asl bits
|
||||
bne DgEnd
|
||||
jsr GetNewBits
|
||||
DgEnd:
|
||||
}
|
||||
|
||||
.macro GetLen() {
|
||||
lda #1
|
||||
GlLoop:
|
||||
:GetNextBit()
|
||||
bcc GlEnd
|
||||
:GetNextBit()
|
||||
rol
|
||||
bpl GlLoop
|
||||
GlEnd:
|
||||
}
|
||||
|
||||
Decrunch:
|
||||
sty Get1+1
|
||||
sty Get2+1
|
||||
sty Get3+1
|
||||
stx Get1+2
|
||||
stx Get2+2
|
||||
stx Get3+2
|
||||
|
||||
ldx #0
|
||||
jsr GetNewBits
|
||||
sty put-1,x
|
||||
cpx #2
|
||||
bcc *-7
|
||||
lda #$80
|
||||
sta bits
|
||||
DLoop:
|
||||
:GetNextBit()
|
||||
bcs Match
|
||||
Literal:
|
||||
// Literal run.. get length.
|
||||
:GetLen()
|
||||
sta LLen+1
|
||||
|
||||
ldy #0
|
||||
LLoop:
|
||||
Get3:
|
||||
lda $feed,x
|
||||
inx
|
||||
bne *+5
|
||||
jsr GnbInc
|
||||
L1: sta (put),y
|
||||
iny
|
||||
LLen:
|
||||
cpy #0
|
||||
bne LLoop
|
||||
|
||||
clc
|
||||
tya
|
||||
adc put
|
||||
sta put
|
||||
bcc *+4
|
||||
inc put+1
|
||||
|
||||
iny
|
||||
beq DLoop
|
||||
|
||||
// Has to continue with a match..
|
||||
|
||||
Match:
|
||||
// Match.. get length.
|
||||
:GetLen()
|
||||
sta MLen+1
|
||||
|
||||
// Length 255 -> EOF
|
||||
cmp #$ff
|
||||
beq End
|
||||
|
||||
// Get num bits
|
||||
cmp #2
|
||||
lda #0
|
||||
rol
|
||||
:GetNextBit()
|
||||
rol
|
||||
:GetNextBit()
|
||||
rol
|
||||
tay
|
||||
lda Tab,y
|
||||
beq M8
|
||||
|
||||
// Get bits < 8
|
||||
M_1:
|
||||
:GetNextBit()
|
||||
rol
|
||||
bcs M_1
|
||||
bmi MShort
|
||||
M8:
|
||||
// Get byte
|
||||
eor #$ff
|
||||
tay
|
||||
Get2:
|
||||
lda $feed,x
|
||||
inx
|
||||
bne *+5
|
||||
jsr GnbInc
|
||||
jmp Mdone
|
||||
MShort:
|
||||
ldy #$ff
|
||||
Mdone:
|
||||
//clc
|
||||
adc put
|
||||
sta MLda+1
|
||||
tya
|
||||
adc put+1
|
||||
sta MLda+2
|
||||
|
||||
ldy #$ff
|
||||
MLoop:
|
||||
iny
|
||||
MLda:
|
||||
lda $beef,y
|
||||
sta (put),y
|
||||
MLen:
|
||||
cpy #0
|
||||
bne MLoop
|
||||
|
||||
//sec
|
||||
tya
|
||||
adc put
|
||||
sta put
|
||||
bcc *+4
|
||||
inc put+1
|
||||
|
||||
jmp DLoop
|
||||
|
||||
End:
|
||||
rts
|
||||
|
||||
GetNewBits:
|
||||
Get1:
|
||||
ldy $feed,x
|
||||
sty bits
|
||||
rol bits
|
||||
inx
|
||||
bne GnbEnd
|
||||
GnbInc:
|
||||
inc Get1+2
|
||||
inc Get2+2
|
||||
inc Get3+2
|
||||
GnbEnd:
|
||||
rts
|
||||
|
||||
Tab:
|
||||
// Short offsets
|
||||
.byte %11011111 // 3
|
||||
.byte %11111011 // 6
|
||||
.byte %00000000 // 8
|
||||
.byte %10000000 // 10
|
||||
// Long offsets
|
||||
.byte %11101111 // 4
|
||||
.byte %11111101 // 7
|
||||
.byte %10000000 // 10
|
||||
.byte %11110000 // 13
|
13
src/test/kc/complex/new_30_years_low_resolution/demo.h
Normal file
13
src/test/kc/complex/new_30_years_low_resolution/demo.h
Normal file
@ -0,0 +1,13 @@
|
||||
// Demo Flow Engine
|
||||
|
||||
// Start the demo IRQ. Can be called multiple times!
|
||||
// Setting IRQ to the "demo" IRQ running outside the parts and
|
||||
// Setting memory to IO + RAM (no kernal/basic)
|
||||
void demo_start();
|
||||
|
||||
// Work to be performed every frame while the demo runs
|
||||
// Assumes that I/O is enabled
|
||||
void demo_work();
|
||||
|
||||
// Counts total demo frames
|
||||
extern volatile unsigned int demo_frame_count;
|
19
src/test/kc/complex/new_30_years_low_resolution/demo.ld
Normal file
19
src/test/kc/complex/new_30_years_low_resolution/demo.ld
Normal file
@ -0,0 +1,19 @@
|
||||
// Commodore 64 PRG executable file
|
||||
.plugin "se.triad.kickass.CruncherPlugins"
|
||||
.file [name="%O", type="prg", segments="Program"]
|
||||
.segmentdef Program [segments="Basic, Common, Part2, Part1, InitDemo"]
|
||||
.segmentdef Basic [start=$0801]
|
||||
.segmentdef Common [segments="Code, Data"]
|
||||
.segmentdef Code [start=%P]
|
||||
.segmentdef Data [startAfter="Code"]
|
||||
.segmentdef Part2 [segments="CodePart2, DataPart2, InitPart2"]
|
||||
.segmentdef CodePart2 [startAfter="Data"]
|
||||
.segmentdef DataPart2 [startAfter="CodePart2"]
|
||||
.segmentdef InitPart2 [startAfter="DataPart2"]
|
||||
.segmentdef Part1 [segments="CodePart1, DataPart1, InitPart1"]
|
||||
.segmentdef CodePart1 [startAfter="InitPart2"]
|
||||
.segmentdef DataPart1 [startAfter="CodePart1"]
|
||||
.segmentdef InitPart1 [startAfter="DataPart1"]
|
||||
.segmentdef InitDemo [startAfter="InitPart1"]
|
||||
.segment Basic
|
||||
:BasicUpstart(%E)
|
Binary file not shown.
Binary file not shown.
After Width: | Height: | Size: 14 KiB |
Binary file not shown.
After Width: | Height: | Size: 17 KiB |
168
src/test/kc/complex/new_30_years_low_resolution/mcbitmap.asm
Normal file
168
src/test/kc/complex/new_30_years_low_resolution/mcbitmap.asm
Normal file
@ -0,0 +1,168 @@
|
||||
// KickAssembler functions for converting a Picture to a multi-color bitmap
|
||||
// Based on Code by Cruzer/CML, April 2011 (https://codebase64.org/doku.php?id=base:double_screen_horizontal_scroller&s[]=koala)
|
||||
// Based on a routine from Codebase64 by Martin Piper
|
||||
//---------------------------------------------------------------------------------------------------------------------
|
||||
// Picture - a result from LoadPicture()
|
||||
// - The upper line is used for defining the c64 palette, with multicolor sized pixels
|
||||
// - The pixel to the right of the palette defines the background color
|
||||
|
||||
// Usage:
|
||||
// #import "mcbitmap.asm"
|
||||
// .var mcBmmData = getMcBitmapData(LoadPicture("bitmap.png"))
|
||||
// *=$0c00 "SCREEN";
|
||||
// .for (var y=0; y<25; y++)
|
||||
// .for (var x=0; x<40; x++)
|
||||
// .byte getMcScreenData(x, y, mcBmmData)
|
||||
// *=$1c00 "COLORS"
|
||||
// colorRam:
|
||||
// .for (var y=0; y<25; y++)
|
||||
// .for (var x=0; x<40; x++)
|
||||
// .byte getMcColorData(x, y, mcBmmData)
|
||||
// *=$2000 "BITMAP";
|
||||
// .for (var y=0; y<25; y++)
|
||||
// .for (var x=0; x<40; x++)
|
||||
// .fill 8, getMcPixelData(x, y, i, mcBmmData)
|
||||
|
||||
#importonce
|
||||
|
||||
.struct McBitmapPalette {
|
||||
// Hashtable mapping RGB to C64 color
|
||||
// rgb2c64.get(rgb) returns a C64 color (0-15)
|
||||
rgb2c64,
|
||||
// The (C64) background color of the image
|
||||
bgColor
|
||||
}
|
||||
|
||||
// Bitmap data for a single (4x8 MC pixel) block
|
||||
.struct McBitmapBlock {
|
||||
// List() with 8 pixel bytes
|
||||
pixels,
|
||||
// C64 color for %01 pixels
|
||||
color1,
|
||||
// C64 color for %10 pixels
|
||||
color2,
|
||||
// C64 color for %11 pixels
|
||||
color3,
|
||||
// Byte for screen (color1*$10 + color2)
|
||||
screendata,
|
||||
// Byte for color (color3)
|
||||
colordata
|
||||
}
|
||||
|
||||
// Bitmap data for an entire picture
|
||||
.struct McBitmapData {
|
||||
// The McBitmapPalette
|
||||
palette,
|
||||
// The number of X-blocks (40 for a normal full screen bitmap)
|
||||
width,
|
||||
// The number of Y-blocks (25 for a normal full screen bitmap)
|
||||
height,
|
||||
// List with width*height McBitmapBlock ordered row by row.
|
||||
// blocks.get(y*width+x) is the block at (x,y)
|
||||
blocks
|
||||
}
|
||||
|
||||
// Get multicolor-bitmap data for a picture. The first row of the picture contains the palette and is discarded. (pixel #0-#15 as colors representing C64 color black, white, ... pixel #16 representing the background color)
|
||||
.function getMcBitmapData(pic) {
|
||||
.var palette = findMcPalette(pic)
|
||||
.var width = floor(pic.width/8)
|
||||
.var height = floor((pic.height-1)/8) // -1 to account for the first line containing the palette
|
||||
.var blocks = List()
|
||||
.for (var y=0; y<height; y++)
|
||||
.for (var x=0; x<width; x++)
|
||||
.eval blocks.add(createMcBlock(x, y, pic, palette))
|
||||
.return McBitmapData(palette, width, height, blocks)
|
||||
}
|
||||
|
||||
// Get the screen data for a specific bitmap block (1 char: 4x8 MC pixels). bmData is created by getMcBitmapData()
|
||||
.function getMcScreenData(x, y, bmData) {
|
||||
.return bmData.blocks.get(y*bmData.width + x).screendata
|
||||
}
|
||||
|
||||
// Get the color data for a specific bitmap block (1 char: 4x8 MC pixels). bmData is created by getMcBitmapData()
|
||||
.function getMcColorData(x, y, bmData) {
|
||||
.return bmData.blocks.get(y*bmData.width + x).colordata
|
||||
}
|
||||
|
||||
// Get pixel data for a specific bitmap block (1 char: 4x8 MC pixels). i is the pixel row (0-7). bmData is created by getMcBitmapData()
|
||||
.function getMcPixelData(x, y, i, bmData) {
|
||||
.return bmData.blocks.get(y*bmData.width + x).pixels.get(i)
|
||||
}
|
||||
|
||||
// Find the C64 palette RGB values to use.
|
||||
// Looks at the first 16 pixels in the first line (0,0)..(0,15) of an image to find the palette.
|
||||
// Looks at pixel number 17 in the first line (0,16) to identify the background color to use.
|
||||
// Returns McBitmapPalette
|
||||
.function findMcPalette(pic) {
|
||||
.var rgb2c64 = Hashtable()
|
||||
.for (var i=0; i<16; i++) {
|
||||
.var rgb = pic.getPixel(i*2,0)
|
||||
.eval rgb2c64.put(rgb, i)
|
||||
}
|
||||
.var bgRgb = pic.getPixel(16*2,0)
|
||||
.var bgColor = rgb2c64.get(bgRgb)
|
||||
.return McBitmapPalette(rgb2c64, bgColor)
|
||||
}
|
||||
|
||||
// Get the C64 color for a specific pixel (0-15)
|
||||
// Assumes multicolor with 2-pixel wide
|
||||
// Uses the passed palette to identify the C64 color
|
||||
.function getMcC64Color(xPos, yPos, pic, palette) {
|
||||
.var rgb = pic.getPixel(xPos*2, yPos+1)
|
||||
.var c64Color = palette.rgb2c64.get(rgb)
|
||||
.return c64Color
|
||||
}
|
||||
|
||||
// Get the C64 colors to use for a bitmap block (1 char: 4x8 MC pixels).
|
||||
// Returns a list with 16 values (one per C64 color).
|
||||
// The values in the list is 0 for unused (or bgColor) and 1, 2, 3 for each used color.
|
||||
// cols.get(c64Color) will return %01, %10, %11 when passed the C64 color matching each multi-color. This is exactly the pixel value to use for the color.
|
||||
.function findMcBlockColors(chrX, chrY, pic, palette) {
|
||||
.var colorCounts = List()
|
||||
.for (var i=0; i<16; i++) .eval colorCounts.add(0)
|
||||
.for (var pixY=0; pixY<8; pixY++) {
|
||||
.for (var pixX=0; pixX<4; pixX++) {
|
||||
.var c64Color = getMcC64Color(chrX*4 + pixX, chrY*8 + pixY, pic, palette)
|
||||
.eval colorCounts.set(c64Color, colorCounts.get(c64Color) + 1)
|
||||
}
|
||||
}
|
||||
.eval colorCounts.set(palette.bgColor,0)
|
||||
.for (var i=0; i<16; i++) .eval colorCounts.set(i, [colorCounts.get(i) << 4] | i)
|
||||
.eval colorCounts.sort()
|
||||
.eval colorCounts.reverse()
|
||||
.var blockColors = List()
|
||||
.for (var i=0; i<16; i++) .eval blockColors.add(0)
|
||||
.for (var i=0; i<3; i++) {
|
||||
.var c64Color = colorCounts.get(i) & $0f
|
||||
.eval blockColors.set(c64Color, i+1)
|
||||
}
|
||||
.return blockColors
|
||||
}
|
||||
|
||||
// Get the bitmap bytes for a a bitmap block (1 char: 4x8 MC pixels).
|
||||
// Returns BitmapBlock containing pixel data and color data for the bitmap block
|
||||
.function createMcBlock(chrX, chrY, pic, palette) {
|
||||
.var blockColors = findMcBlockColors(chrX, chrY, pic, palette)
|
||||
.var pixelData = List()
|
||||
.for (var pixY=0; pixY<8; pixY++) {
|
||||
.var bitmapByte = 0
|
||||
.for (var pixX=0; pixX<4; pixX++) {
|
||||
.var c64Color = getMcC64Color(chrX*4 + pixX, chrY*8 + pixY, pic, palette)
|
||||
.var multiColor = blockColors.get(c64Color)
|
||||
.eval bitmapByte = bitmapByte | [multiColor << [6 - pixX*2]]
|
||||
}
|
||||
.eval pixelData.add(bitmapByte)
|
||||
}
|
||||
.var bitmapBlock = McBitmapBlock()
|
||||
.eval bitmapBlock.pixels = pixelData
|
||||
// Find the three C64 multi-colors
|
||||
.for (var i=1; i<16; i++) {
|
||||
.if (blockColors.get(i) == %01) .eval bitmapBlock.color1 = i
|
||||
.if (blockColors.get(i) == %10) .eval bitmapBlock.color2 = i
|
||||
.if (blockColors.get(i) == %11) .eval bitmapBlock.color3 = i
|
||||
}
|
||||
// Convert multi-colors to screen data and color data
|
||||
.eval bitmapBlock.screendata = (bitmapBlock.color1 << 4) | (bitmapBlock.color2 & $f)
|
||||
.eval bitmapBlock.colordata = bitmapBlock.color3
|
||||
.return bitmapBlock
|
||||
}
|
@ -0,0 +1,195 @@
|
||||
// A bucket-sorting sprite multiplexer
|
||||
// A multiplexer frame contains a fixed number of multiplexer buckets.
|
||||
// Each multiplexer bucket represents a single raster line (typically an IRQ) where sprites are positioned.
|
||||
// Each multiplexer bucket has the capability of moving 0-8 sprites.
|
||||
// The bucket sorting multiplexer is used for cyclic sprite Y-movements.
|
||||
// (eg. 32 sprites in a single large 256 byte sine results in a 8 frame cycle after which the sprites can swap places and repeat the same 8 frames again to give one continous movement).
|
||||
// ## Pre-calculation
|
||||
// It works by pre-calculating frames and buckets into arrays of struct BucketSprite - and then using these buckets when rendering the actual multiplexer.
|
||||
// Only the Y-positions (in PLEX_YPOS) are used for pre-calculation.
|
||||
// Set-up is done by
|
||||
// 1. Defining PLEX_COUNT as the number of sprites in the multiplexer
|
||||
// 2. Defining BUCKET_COUNT as the number of buckets per frame
|
||||
// 3. Setting BUCKET_YPOS to the Y-positions of each bucket raster line
|
||||
// Pre-calculation is done by
|
||||
// 1. Call plexBucketInit()
|
||||
// 2. For each frame:
|
||||
// a. Set up Y-positions in PLEX_YPOS
|
||||
// b. Call plexBucketSort(struct BucketSprite* frame) where frame is a BUCKET_COUNT*BUCKET_SIZE array that will receive sorted sprite-data for rendering the frame.
|
||||
// Rendering is done by the following for each frame:
|
||||
// 1. Set up X-positions (in PLEX_XPOS and PLEX_XPOS_MSB) and pointers (in PLEX_PTR). If they are unchanged then you do not need to change them.
|
||||
// 2. Call plexBucketFrameInit()
|
||||
// 3. For each bucket.
|
||||
// a. Wait for the raster line in BUCKET_YPOS
|
||||
// b. Call plexBucketShow(bucket) where bucket is the frame array prepared in the pre-calculation.
|
||||
// c. Move to the next bucket in the frame by doing
|
||||
// bucket += BUCKET_SIZE;
|
||||
// 4. After rendering all buckets of the frame, update the plex_id's of all the sprites in all the buckets.
|
||||
// This is done to prepare for the next cycle where all sprites swap places to give a continous movement.
|
||||
// Typically you want to do
|
||||
// sprite->plex_id=(sprite->plex_id-1)&(FRAME_COUNT-1)
|
||||
|
||||
#include "multiplex-bucket.h"
|
||||
#include <c64.h>
|
||||
|
||||
#ifdef DEBUG_PLEX
|
||||
#include <stdio.h>
|
||||
#endif
|
||||
|
||||
#ifndef PLEX_SPRITE_PTRS
|
||||
#define PLEX_SPRITE_PTRS DEFAULT_SCREEN+OFFSET_SPRITE_PTRS
|
||||
#endif
|
||||
|
||||
// The screen sprite pointers to update
|
||||
char * const SCREEN_SPRITE_PTRS = PLEX_SPRITE_PTRS;
|
||||
|
||||
// The Y-position (IRQ raster line) starting each bucket
|
||||
char BUCKET_YPOS[BUCKET_COUNT] = { 0x10, 0x48, 0x58, 0x72, 0x8e, 0xaa, 0xc0, 0xd0, 0xde };
|
||||
|
||||
// The y-positions of the multiplexer sprites. (These are converted to multiplexer buckets)
|
||||
char PLEX_YPOS[PLEX_COUNT];
|
||||
|
||||
// The low byte of the x-positions of the multiplexer sprites
|
||||
char PLEX_XPOS[PLEX_COUNT];
|
||||
|
||||
// The MSB of the x-positions of the multiplexer sprites (0/1)
|
||||
char PLEX_XPOS_MSB[PLEX_COUNT];
|
||||
|
||||
// The sprite pointers for the multiplexed sprites
|
||||
char PLEX_PTR[PLEX_COUNT];
|
||||
|
||||
// The sprite color for the multiplexed sprites
|
||||
//char PLEX_COL[PLEX_COUNT];
|
||||
|
||||
// Indexes of the plex-sprites sorted by sprite y-position. Each call to plexSort() will fix the sorting if changes to the Y-positions have ruined it.
|
||||
char PLEX_SORTED_IDX[PLEX_COUNT];
|
||||
|
||||
// Initialize data structures for the multiplexer
|
||||
void plexPrepareInit() {
|
||||
// Initial sorting is trivial
|
||||
for(char i=0; i<PLEX_COUNT;i++)
|
||||
PLEX_SORTED_IDX[i] = i;
|
||||
}
|
||||
|
||||
// Performs run-time bucket sort of the sprites in the PLEX_ arrays into struct BucketSprite[]
|
||||
// Starts by performing a true sorting of the sprites based on Y-position (unsing insertion sort)
|
||||
void plexPrepareFrame(struct BucketSprite* frame) {
|
||||
// Sort the sprite indices in PLEX_SORTED_IDX based on the Y-position (using insertion sort)
|
||||
// Assumes that the positions are nearly sorted already (as each sprite just moves a bit)
|
||||
// 1. Moves a marker (m) from the start to end of the array. Every time the marker moves forward all elements before the marker are sorted correctly.
|
||||
// 2a. If the next element after the marker is larger that the current element
|
||||
// the marker can be moved forwards (as the sorting is correct).
|
||||
// 2b. If the next element after the marker is smaller than the current element:
|
||||
// elements before the marker are shifted right one at a time until encountering one smaller than the current one.
|
||||
// It is then inserted at the spot. Now the marker can move forward.
|
||||
for(char m=0;m<PLEX_COUNT-1;m++) {
|
||||
char nxt_idx = PLEX_SORTED_IDX[m+1];
|
||||
char nxt_y = PLEX_YPOS[nxt_idx];
|
||||
if(nxt_y<PLEX_YPOS[PLEX_SORTED_IDX[m]]) {
|
||||
// Shift values until we encounter a value smaller than nxt_y
|
||||
char s = m;
|
||||
do {
|
||||
PLEX_SORTED_IDX[s+1] = PLEX_SORTED_IDX[s];
|
||||
s--;
|
||||
} while((s!=0xff) && (nxt_y<PLEX_YPOS[PLEX_SORTED_IDX[s]]));
|
||||
// store the mark at the found position
|
||||
s++;
|
||||
PLEX_SORTED_IDX[s] = nxt_idx;
|
||||
}
|
||||
}
|
||||
// Y-position where each real sprite is free (used for selecting the best bucket)
|
||||
char real_sprite_free_ypos[8];
|
||||
// Initialize real sprite free to the first bucket Y-position
|
||||
for(char i=0;i<8;i++) real_sprite_free_ypos[i] = BUCKET_YPOS[0];
|
||||
// The current real sprite idx
|
||||
char real_sprite_id = 0;
|
||||
// The current bucket idx
|
||||
char bucket_id = 0;
|
||||
// The current bucket start y-position
|
||||
char bucket_ypos = BUCKET_YPOS[bucket_id];
|
||||
// The current bucket & sprite
|
||||
struct BucketSprite *bucket = frame, *sprite = frame;
|
||||
for(char i=0;i<PLEX_COUNT; i++) {
|
||||
char plex_id = PLEX_SORTED_IDX[i];
|
||||
unsigned char ypos = PLEX_YPOS[plex_id];
|
||||
if(real_sprite_free_ypos[real_sprite_id] > bucket_ypos) {
|
||||
// The real sprite is not free in the current bucket - move to the next bucket!
|
||||
#ifdef DEBUG_PLEX
|
||||
if(bucket_id>=BUCKET_COUNT-1) printf("plex#%hhx ypos:%hhx not free in last bucket#%hhx ypos:%hhx. real sprite#%hhx free at ypos %hhx.\n", plex_id, ypos, bucket_id, bucket_ypos, real_sprite_id, real_sprite_free_ypos[real_sprite_id]);
|
||||
#endif
|
||||
// Move to the next bucket
|
||||
bucket_id++;
|
||||
bucket_ypos = BUCKET_YPOS[bucket_id];
|
||||
bucket += BUCKET_SIZE;
|
||||
// Zero-fill the next sprite in the bucket (if not full)
|
||||
if(sprite!=bucket) sprite->ypos=0;
|
||||
// Set current sprite to start of next bucket
|
||||
sprite = bucket;
|
||||
}
|
||||
// Identify problems filling buckets
|
||||
#ifdef DEBUG_PLEX
|
||||
if(ypos<=bucket_ypos) printf("plex#%hhx ypos:%hhx <= bucket#%hhx ypos:%hhx. lower bucket ypos or introduce new with lower ypos.\n", plex_id, ypos, bucket_id, bucket_ypos);
|
||||
#endif
|
||||
// Put the sprite into the bucket
|
||||
sprite->ypos = ypos;
|
||||
sprite->plex_id = plex_id;
|
||||
// Increase bucket ypos to account for time spent placing the sprite
|
||||
bucket_ypos += 1;
|
||||
// Update next free ypos for the real sprite
|
||||
real_sprite_free_ypos[real_sprite_id] = ypos+22;
|
||||
// Move to the next real sprite
|
||||
real_sprite_id = (real_sprite_id+1)&7;
|
||||
// Move to the next sprite in the bucket
|
||||
sprite++;
|
||||
}
|
||||
// Zero-fill the next sprite in the bucket (if not full)
|
||||
bucket += BUCKET_SIZE;
|
||||
if(sprite!=bucket) sprite->ypos=0;
|
||||
}
|
||||
|
||||
// The next "real" sprite being used by the multiplexer
|
||||
volatile char plex_real_sprite_idx = 0;
|
||||
|
||||
// Start a new frame (initializing data structures)
|
||||
void plexFrameStart() {
|
||||
plex_real_sprite_idx = 0;
|
||||
}
|
||||
|
||||
// Show the sprites in a specific bucket
|
||||
// - bucket: The bucket to show
|
||||
void plexBucketShow(struct BucketSprite* bucket) {
|
||||
// Masks used for MSB
|
||||
char MSB_SET_MASK_BY_ID[8] = { 0x01, 0x02, 0x04, 0x08, 0x10, 0x20, 0x40, 0x80 };
|
||||
char MSB_CLEAR_MASK_BY_ID[8] = { 0xfe, 0xfd, 0xfb, 0xf7, 0xef, 0xdf, 0xbf, 0x7f };
|
||||
// Use a char* to optimize the code!
|
||||
char* bucket_ptr = (char*)bucket;
|
||||
char real_idx = plex_real_sprite_idx*2;
|
||||
char i=0;
|
||||
while(bucket_ptr[i]) {
|
||||
SPRITES_YPOS[real_idx] = bucket_ptr[i++];
|
||||
char plex_id = bucket_ptr[i];
|
||||
SPRITES_XPOS[real_idx] = PLEX_XPOS[plex_id];
|
||||
real_idx /= 2;
|
||||
if(PLEX_XPOS_MSB[plex_id]) {
|
||||
*SPRITES_XMSB |= MSB_SET_MASK_BY_ID[real_idx];
|
||||
} else {
|
||||
*SPRITES_XMSB &= MSB_CLEAR_MASK_BY_ID[real_idx];
|
||||
}
|
||||
SCREEN_SPRITE_PTRS[real_idx] = PLEX_PTR[plex_id];
|
||||
//SPRITES_COLOR[real_idx] = PLEX_COL[plex_id];
|
||||
real_idx = (real_idx+1)&7;
|
||||
real_idx *= 2;
|
||||
i++;
|
||||
if(i==BUCKET_SIZE*sizeof(struct BucketSprite)) break;
|
||||
}
|
||||
plex_real_sprite_idx = real_idx/2;
|
||||
}
|
||||
|
||||
// Updates the PLEX_ID's preparing for the next cycle of the multiplexer.
|
||||
// Performs: sprite->plex_id=(sprite->plex_id-1)&(FRAME_COUNT-1)
|
||||
void plexFrameEnd(struct BucketSprite* frame) {
|
||||
char* sprite_plex_ids = &frame->plex_id;
|
||||
for(char i=0;i<BUCKET_COUNT*BUCKET_SIZE*2;i+=2) {
|
||||
sprite_plex_ids[i] = (sprite_plex_ids[i]-1)&(PLEX_COUNT-1);
|
||||
}
|
||||
}
|
@ -0,0 +1,81 @@
|
||||
// A bucket-sorting sprite multiplexer
|
||||
// A multiplexer frame contains a fixed number of multiplexer buckets.
|
||||
// Each multiplexer bucket represents a single raster line (typically an IRQ) where sprites are positioned.
|
||||
// Each multiplexer bucket has the capability of moving 0-8 sprites.
|
||||
// The bucket sorting multiplexer is used for cyclic sprite Y-movements.
|
||||
// (eg. 32 sprites in a single large 256 byte sine results in a 8 frame cycle after which the sprites can swap places and repeat the same 8 frames again to give one continous movement).
|
||||
// ## Pre-calculation
|
||||
// It works by pre-calculating frames and buckets into arrays of struct BucketSprite - and then using these buckets when rendering the actual multiplexer.
|
||||
// Only the Y-positions (in PLEX_YPOS) are used for pre-calculation.
|
||||
// Set-up is done by
|
||||
// 1. Defining PLEX_COUNT as the number of sprites in the multiplexer
|
||||
// 2. Defining BUCKET_COUNT as the number of buckets per frame
|
||||
// 3. Setting BUCKET_YPOS to the Y-positions of each bucket raster line
|
||||
// Pre-calculation is done by
|
||||
// 1. Call plexBucketInit()
|
||||
// 2. For each frame:
|
||||
// a. Set up Y-positions in PLEX_YPOS
|
||||
// b. Call plexBucketSort(struct BucketSprite* frame) where frame is a BUCKET_COUNT*BUCKET_SIZE array that will receive sorted sprite-data for rendering the frame.
|
||||
// Rendering is done by the following for each frame:
|
||||
// 1. Set up X-positions (in PLEX_XPOS and PLEX_XPOS_MSB) and pointers (in PLEX_PTR). If they are unchanged then you do not need to change them.
|
||||
// 2. Call plexBucketFrameInit()
|
||||
// 3. For each bucket.
|
||||
// a. Wait for the raster line in BUCKET_YPOS
|
||||
// b. Call plexBucketShow(bucket) where bucket is the frame array prepared in the pre-calculation.
|
||||
// c. Move to the next bucket in the frame by doing
|
||||
// bucket += BUCKET_SIZE;
|
||||
// 4. After rendering all buckets of the frame, update the plex_id's of all the sprites in all the buckets.
|
||||
// This is done to prepare for the next cycle where all sprites swap places to give a continous movement.
|
||||
// Typically you want to do
|
||||
// sprite->plex_id=(sprite->plex_id-1)&(FRAME_COUNT-1)
|
||||
|
||||
// The number of sprites in the multiplexer
|
||||
#define PLEX_COUNT 32
|
||||
// The number of sprites per multiplexer bucket
|
||||
#define BUCKET_SIZE 8
|
||||
// The number of multiplexer buckets per frame
|
||||
#define BUCKET_COUNT 9
|
||||
// The number of multiplexer frames
|
||||
#define FRAME_COUNT 8
|
||||
|
||||
// The Y-position (IRQ raster line) starting each bucket
|
||||
extern char BUCKET_YPOS[BUCKET_COUNT];
|
||||
|
||||
// The y-positions of the multiplexer sprites. (These are converted to multiplexer buckets)
|
||||
extern char PLEX_YPOS[PLEX_COUNT];
|
||||
|
||||
// The low byte of the x-positions of the multiplexer sprites
|
||||
extern char PLEX_XPOS[PLEX_COUNT];
|
||||
|
||||
// The MSB of the x-positions of the multiplexer sprites (0/1)
|
||||
extern char PLEX_XPOS_MSB[PLEX_COUNT];
|
||||
|
||||
// The sprite pointers for the multiplexed sprites
|
||||
extern char PLEX_PTR[PLEX_COUNT];
|
||||
|
||||
// The sprite color for the multiplexed sprites
|
||||
//extern char PLEX_COL[PLEX_COUNT];
|
||||
|
||||
// A single sprite in a multiplexer bucket
|
||||
struct BucketSprite {
|
||||
char ypos;
|
||||
char plex_id;
|
||||
};
|
||||
|
||||
// Initialize data structures for the multiplexer
|
||||
void plexPrepareInit();
|
||||
|
||||
// Performs run-time bucket sort of the sprites in the PLEX_ arrays into struct BucketSprite[]
|
||||
// Starts by performing a true sorting of the sprites based on Y-position (using insertion sort)
|
||||
void plexPrepareFrame(struct BucketSprite* frame);
|
||||
|
||||
// Start a new frame (initializing data structures)
|
||||
void plexFrameStart();
|
||||
|
||||
// Show the sprites in a specific bucket
|
||||
// - bucketSprites: The bucket to show
|
||||
void plexBucketShow(struct BucketSprite* bucket);
|
||||
|
||||
// Updates the PLEX_ID's preparing for the next cycle of the multiplexer.
|
||||
// Performs: sprite->plex_id=(sprite->plex_id-1)&(FRAME_COUNT-1)
|
||||
void plexFrameEnd(struct BucketSprite* frame);
|
@ -0,0 +1,142 @@
|
||||
// The Demo collects the parts and handles overall control
|
||||
|
||||
#pragma target(c64)
|
||||
#pragma link("demo.ld")
|
||||
#pragma zp_reserve(0xfa..0xfb) // Reserved for music player
|
||||
#pragma interrupt(hardware_clobber)
|
||||
#pragma emulator("C64Debugger")
|
||||
|
||||
#include <c64.h>
|
||||
#include <6502.h>
|
||||
#include <string.h>
|
||||
#include "demo.h"
|
||||
#include "byteboozer.h"
|
||||
#include "part1-happynewyear.c"
|
||||
#include "part2-swingplex.c"
|
||||
|
||||
#pragma code_seg(Code)
|
||||
#pragma data_seg(Data)
|
||||
|
||||
char* const DEMO_MUSIC = 0xAC00;
|
||||
// Pointer to the music init routine
|
||||
void()* const musicInit = (void()*) DEMO_MUSIC;
|
||||
// Pointer to the music play routine
|
||||
void()* const musicPlay = (void()*) DEMO_MUSIC+3;
|
||||
|
||||
#pragma data_seg(InitDemo)
|
||||
|
||||
// SID tune
|
||||
char DEMO_MUSIC_CRUNCHED[] = kickasm(resource "do-it-again-$AC00-$FA-8580.sid", uses DEMO_MUSIC) {{
|
||||
.modify B2() {
|
||||
.pc = DEMO_MUSIC "MUSIC"
|
||||
.const music = LoadSid("do-it-again-$AC00-$FA-8580.sid")
|
||||
.fill music.size, music.getData(i)
|
||||
}
|
||||
}};
|
||||
|
||||
#pragma data_seg(Data)
|
||||
|
||||
void main() {
|
||||
// Initialize the demo - start the IRQ
|
||||
demo_init();
|
||||
// Decrunch music
|
||||
byteboozer_decrunch(DEMO_MUSIC_CRUNCHED);
|
||||
// Init music
|
||||
asm { lda #0 }
|
||||
(*musicInit)();
|
||||
// Initialize the demo - start the IRQ
|
||||
demo_start();
|
||||
// Initialize Part 1 (Revealing "Happy New Year" logo)
|
||||
part1_init();
|
||||
// Start part 1 at 0:04,5
|
||||
while(demo_frame_count<5*50) ;
|
||||
// Run Part 1 (Revealing "Happy New Year" logo)
|
||||
part1_run();
|
||||
// Initialize part 2
|
||||
part2_init();
|
||||
// Wait for the right place to start part 2
|
||||
while(demo_frame_count<16*50) ;
|
||||
// Disable sparkler
|
||||
sparkler_active = 0;
|
||||
// Run part 2
|
||||
part2_run();
|
||||
|
||||
for(;;) ;
|
||||
|
||||
}
|
||||
|
||||
// Initialize demo code.
|
||||
// Can be called multiple times!
|
||||
// Setting IRQ to the "demo" IRQ running outside the parts and
|
||||
// Setting memory to IO + RAM (no kernal/basic)
|
||||
void demo_init() {
|
||||
SEI();
|
||||
// Disable kernal & basic
|
||||
*PROCPORT_DDR = PROCPORT_DDR_MEMORY_MASK;
|
||||
*PROCPORT = PROCPORT_RAM_IO;
|
||||
// Disable CIA 1 Timer IRQ
|
||||
CIA1->INTERRUPT = CIA_INTERRUPT_CLEAR;
|
||||
// Acknowledge any timer IRQ
|
||||
asm { lda CIA1_INTERRUPT }
|
||||
// Acknowledge any VIC IRQ
|
||||
*IRQ_STATUS = 0x0f;
|
||||
}
|
||||
|
||||
// Start the demo IRQ. Can be called multiple times!
|
||||
// Setting IRQ to the "demo" IRQ running outside the parts and
|
||||
// Setting memory to IO + RAM (no kernal/basic)
|
||||
void demo_start() {
|
||||
demo_init();
|
||||
// Set raster line to 0x00
|
||||
*VICII_CONTROL &= 0x7f;
|
||||
*RASTER = 0;
|
||||
// Set the IRQ routine
|
||||
*HARDWARE_IRQ = &irq_demo;
|
||||
// Enable Raster Interrupt
|
||||
*IRQ_ENABLE = IRQ_RASTER;
|
||||
CLI();
|
||||
}
|
||||
|
||||
// Counts total demo frames
|
||||
volatile unsigned int demo_frame_count = 0;
|
||||
|
||||
// Work to be performed every frame while the demo runs
|
||||
// Assumes that I/O is enabled
|
||||
void demo_work() {
|
||||
// Increase frame count
|
||||
demo_frame_count++;
|
||||
// Play music
|
||||
(*musicPlay)();
|
||||
// Animate the sparkler
|
||||
if(sparkler_active)
|
||||
sparkler_anim();
|
||||
}
|
||||
|
||||
// Is the sparkler active
|
||||
volatile char sparkler_active = 0;
|
||||
|
||||
// The sparkler sprite idx
|
||||
volatile char sparkler_idx = 0;
|
||||
|
||||
// Animate the sparkler sprite
|
||||
void sparkler_anim() {
|
||||
if(++sparkler_idx==30) sparkler_idx=0;
|
||||
P1_SCREEN_SPRITE_PTRS[0] = toSpritePtr(P1_SPRITES)+sparkler_idx/2;
|
||||
}
|
||||
|
||||
// IRQ running during between parts
|
||||
__interrupt void irq_demo() {
|
||||
// Remember processor port value
|
||||
*PROCPORT_DDR = PROCPORT_DDR_MEMORY_MASK;
|
||||
char port_value = *PROCPORT;
|
||||
// Enable IO
|
||||
*PROCPORT_DDR = PROCPORT_DDR_MEMORY_MASK;
|
||||
*PROCPORT = PROCPORT_RAM_IO;
|
||||
// Perform any demo work
|
||||
demo_work();
|
||||
// Acknowledge the IRQ
|
||||
*IRQ_STATUS = IRQ_RASTER;
|
||||
// Restore processor port value
|
||||
*PROCPORT_DDR = PROCPORT_DDR_MEMORY_MASK;
|
||||
*PROCPORT = port_value;
|
||||
}
|
@ -0,0 +1,355 @@
|
||||
// Show the Happy New Year image as a MC bitmap
|
||||
#include <c64.h>
|
||||
#include <6502.h>
|
||||
#include <string.h>
|
||||
#include "byteboozer.h"
|
||||
|
||||
#pragma code_seg(CodePart1)
|
||||
#pragma data_seg(DataPart1)
|
||||
|
||||
char * const P1_COLORS = 0xa800; // A800-AFFF
|
||||
char * const P1_PIXELS = 0xc000; // C000-DFFF
|
||||
char * const P1_SCREEN = 0xe000; // E000-E3FF
|
||||
char * const P1_SPRITES = 0xfc00; // E000-E3FF
|
||||
char * const PIXELS_EMPTY = 0xe800; // E800-EFFF
|
||||
// A copy of the load screen and colors
|
||||
char * const LOAD_SCREEN = 0xe400; // E400-E7FF
|
||||
char * const LOAD_CHARSET = 0xf000; // F000-F7FF
|
||||
char * const LOAD_COLORS = 0xf800; // F800-FBFF
|
||||
|
||||
// Flipper cosine easing table
|
||||
unsigned int * const FLIPPER_EASING = 0xa400;
|
||||
|
||||
// Sprite pointers
|
||||
char * const P1_SCREEN_SPRITE_PTRS = 0xe3f8; // P1_SCREEN+OFFSET_SPRITE_PTRS;
|
||||
|
||||
#pragma data_seg(InitPart1)
|
||||
|
||||
// MC Bitmap Data
|
||||
char P1_PIXELS_CRUNCHED[] = kickasm(resource "happy-newyear.png", resource "mcbitmap.asm", uses P1_PIXELS) {{
|
||||
.modify B2() {
|
||||
.pc = P1_PIXELS "HAPPYNEWYEAR PIXELS"
|
||||
#import "mcbitmap.asm"
|
||||
.var mcBmmData1 = getMcBitmapData(LoadPicture("happy-newyear.png"))
|
||||
.for (var y=0; y<25; y++)
|
||||
.for (var x=0; x<40; x++)
|
||||
.fill 8, getMcPixelData(x, y, i, mcBmmData1)
|
||||
}
|
||||
}};
|
||||
|
||||
char P1_SCREEN_CRUNCHED[] = kickasm(uses P1_SCREEN) {{
|
||||
.modify B2() {
|
||||
.pc = P1_SCREEN "HAPPYNEWYEAR SCREEN"
|
||||
.for (var y=0; y<25; y++)
|
||||
.for (var x=0; x<40; x++)
|
||||
.byte getMcScreenData(x, y, mcBmmData1)
|
||||
}
|
||||
}};
|
||||
|
||||
char P1_COLORS_CRUNCHED[] = kickasm(uses P1_COLORS) {{
|
||||
.modify B2() {
|
||||
.pc = P1_COLORS "HAPPYNEWYEAR COLORS"
|
||||
.for (var y=0; y<25; y++)
|
||||
.for (var x=0; x<40; x++)
|
||||
.byte getMcColorData(x, y, mcBmmData1)
|
||||
}
|
||||
}};
|
||||
|
||||
// Sparkler sprites
|
||||
char P1_SPRITES_CRUNCHED[] = kickasm(uses P1_SPRITES, resource "sparklers.png") {{
|
||||
.modify B2() {
|
||||
.pc = P1_SPRITES "P1_SPRITES"
|
||||
// Pixels 11 01 10 11
|
||||
.var p1_sprites = LoadPicture("sparklers.png", List().add($000000, $daccc3, $472a24, $957a71))
|
||||
.for(var sx=0;sx<15;sx++) {
|
||||
.for (var y=0;y<21; y++) {
|
||||
.for (var c=0; c<3; c++) {
|
||||
.byte p1_sprites.getMulticolorByte(sx*3+c,y)
|
||||
}
|
||||
}
|
||||
.byte 0
|
||||
}
|
||||
}
|
||||
}};
|
||||
|
||||
// An easing curve from 0x000 to 0x130
|
||||
export char FLIPPER_EASING_CRUNCHED[] = kickasm {{
|
||||
.modify B2() {
|
||||
.pc = FLIPPER_EASING "FLIPPER_EASING"
|
||||
.fillword $130, round($98+$98*cos(PI+PI*i/$130))
|
||||
}
|
||||
}};
|
||||
|
||||
#pragma data_seg(DataPart1)
|
||||
|
||||
void part1_init() {
|
||||
// Disable IO
|
||||
*PROCPORT_DDR = PROCPORT_DDR_MEMORY_MASK;
|
||||
*PROCPORT = PROCPORT_RAM_ALL;
|
||||
// Decrunch pixels
|
||||
byteboozer_decrunch(P1_PIXELS_CRUNCHED);
|
||||
// Enable IO, Disable kernal & basic
|
||||
*PROCPORT_DDR = PROCPORT_DDR_MEMORY_MASK;
|
||||
*PROCPORT = PROCPORT_RAM_IO;
|
||||
// Decrunch screen
|
||||
byteboozer_decrunch(P1_SCREEN_CRUNCHED);
|
||||
// Decrunch colors
|
||||
byteboozer_decrunch(P1_COLORS_CRUNCHED);
|
||||
// Decrunch sprites
|
||||
byteboozer_decrunch(P1_SPRITES_CRUNCHED);
|
||||
// Decrunch flipper sine table
|
||||
byteboozer_decrunch(FLIPPER_EASING_CRUNCHED);
|
||||
// Initialize the badlines
|
||||
init_rasters();
|
||||
// Fill some empty pixels
|
||||
memset(PIXELS_EMPTY, 0x00, 0x800);
|
||||
// Enable CHARGEN, Disable kernal & basic
|
||||
*PROCPORT_DDR = PROCPORT_DDR_MEMORY_MASK;
|
||||
*PROCPORT = PROCPORT_RAM_CHARROM;
|
||||
memcpy(LOAD_CHARSET, CHARGEN, 0x800);
|
||||
// Enable IO, Disable kernal & basic
|
||||
*PROCPORT_DDR = PROCPORT_DDR_MEMORY_MASK;
|
||||
*PROCPORT = PROCPORT_RAM_IO;
|
||||
// Copy loading screen
|
||||
memcpy(LOAD_SCREEN, DEFAULT_SCREEN, 0x0400);
|
||||
// Copy loading colors
|
||||
memcpy(LOAD_COLORS, COLS, 1000);
|
||||
}
|
||||
|
||||
void part1_run() {
|
||||
SEI();
|
||||
// Disable kernal & basic
|
||||
*PROCPORT_DDR = PROCPORT_DDR_MEMORY_MASK;
|
||||
*PROCPORT = PROCPORT_RAM_IO;
|
||||
// Disable CIA 1 Timer IRQ
|
||||
CIA1->INTERRUPT = CIA_INTERRUPT_CLEAR;
|
||||
// Acknowledge any timer IRQ
|
||||
asm { lda CIA1_INTERRUPT }
|
||||
// Acknowledge any VIC IRQ
|
||||
*IRQ_STATUS = 0x0f;
|
||||
// Set raster line to 0x136
|
||||
*VICII_CONTROL |= 0x80;
|
||||
*RASTER = IRQ_PART1_TOP_LINE;
|
||||
// Set the IRQ routine
|
||||
*HARDWARE_IRQ = &irq_part1_top;
|
||||
// Enable Raster Interrupt
|
||||
*IRQ_ENABLE = IRQ_RASTER;
|
||||
// Show Sparkler
|
||||
VICII->SPRITES_MC = 0x01;
|
||||
VICII->SPRITE0_COLOR = PINK;
|
||||
VICII->SPRITES_MCOLOR1 = YELLOW;
|
||||
VICII->SPRITES_MCOLOR2 = PURPLE;
|
||||
VICII->SPRITES_XMSB = 0x01; // 262
|
||||
VICII->SPRITE0_X = 22; // 262
|
||||
VICII->SPRITE0_Y = 190; // 144
|
||||
P1_SCREEN_SPRITE_PTRS[0] = toSpritePtr(P1_SPRITES);
|
||||
CLI();
|
||||
|
||||
part1_loop();
|
||||
|
||||
}
|
||||
|
||||
// Signals the main() loop to do work when all rasters are complete
|
||||
volatile char p1_work_ready = 0;
|
||||
|
||||
// Handle some stuff in the main() routine
|
||||
void part1_loop() {
|
||||
p1_work_ready = 0;
|
||||
for(;;) {
|
||||
while(p1_work_ready==0) ;
|
||||
// Fix colors
|
||||
flipper_fix_colors();
|
||||
// Show sparkler at 0:09
|
||||
if(!sparkler_active && demo_frame_count>9*50-3) {
|
||||
VICII->SPRITES_ENABLE = 0x01;
|
||||
sparkler_active = 1;
|
||||
}
|
||||
// Perform any demo-wide work
|
||||
demo_work();
|
||||
// Wait for the right place to exit part 1
|
||||
if(demo_frame_count>14*50) {
|
||||
// Re-start the demo base IRQ
|
||||
demo_start();
|
||||
// Leave part 1
|
||||
break;
|
||||
}
|
||||
// My work is done!
|
||||
p1_work_ready = 0;
|
||||
}
|
||||
}
|
||||
|
||||
// Top of the flipper
|
||||
volatile unsigned int irq_flipper_top_line = 0x00;
|
||||
// Bottom of the flipper
|
||||
volatile unsigned int irq_flipper_bottom_line = 0x08;
|
||||
// 1 if flipper is done
|
||||
volatile char flipper_done = 0;
|
||||
|
||||
// 1 if the raster line is a badline
|
||||
__align(0x100) char RASTER_BADLINES[0x130];
|
||||
|
||||
// Initialize the BADLINES
|
||||
void init_rasters() {
|
||||
for(unsigned int i=0;i<sizeof(RASTER_BADLINES);i++)
|
||||
RASTER_BADLINES[i] = 0;
|
||||
for(char b=0x32;b<0xfa;b+=8)
|
||||
RASTER_BADLINES[b] = 1;
|
||||
}
|
||||
|
||||
const char IRQ_PART1_TOP_LINE = 0x36;
|
||||
|
||||
// IRQ running during set-up
|
||||
__interrupt void irq_part1_top() {
|
||||
// Colors
|
||||
VICII->BORDER_COLOR = BLACK;
|
||||
VICII->BG_COLOR = BLACK;
|
||||
// Set BMM
|
||||
VICII->CONTROL1 |= VICII_BMM;
|
||||
// Set MCM
|
||||
VICII->CONTROL2 |= VICII_MCM;
|
||||
// Change graphics bank
|
||||
CIA2->PORT_A = toDd00(P1_SCREEN);
|
||||
// Show screen
|
||||
VICII->MEMORY = toD018(P1_SCREEN, P1_PIXELS);
|
||||
|
||||
// Set up the flipper IRQ
|
||||
if(>irq_flipper_top_line)
|
||||
*VICII_CONTROL |= 0x80;
|
||||
else
|
||||
*VICII_CONTROL &= 0x7f;
|
||||
*RASTER = (<irq_flipper_top_line)&0xf8;
|
||||
*HARDWARE_IRQ = &irq_flipper_top;
|
||||
// Signal main routine to play music
|
||||
p1_work_ready = 1;
|
||||
// Acknowledge the IRQ
|
||||
*IRQ_STATUS = IRQ_RASTER;
|
||||
}
|
||||
|
||||
// IRQ running during set-up
|
||||
// Flips from start screen to bitmap (stops the bitmap)
|
||||
__interrupt void irq_flipper_top() {
|
||||
asm { nop nop nop nop nop nop nop nop nop nop nop nop nop nop nop nop nop nop nop nop nop nop nop }
|
||||
raster_fine((<irq_flipper_top_line)&7);
|
||||
asm {
|
||||
lda #$9a // VICII->MEMORY = toD018(LOAD_SCREEN, PIXELS_EMPTY);
|
||||
ldx #LIGHT_GREEN // VICII->BORDER_COLOR = LIGHT_GREEN;
|
||||
ldy #$1b // VICII->CONTROL1 &= ~VICII_BMM;
|
||||
sta VICII_MEMORY
|
||||
stx BORDER_COLOR
|
||||
sty VICII_CONTROL
|
||||
stx BG_COLOR
|
||||
lda #$c8 // VICII->CONTROL2 &= ~VICII_MCM;
|
||||
sta VICII_CONTROL2
|
||||
}
|
||||
// Set up the flipper IRQ
|
||||
if(>irq_flipper_bottom_line)
|
||||
*VICII_CONTROL |= 0x80;
|
||||
else
|
||||
*VICII_CONTROL &= 0x7f;
|
||||
*RASTER = (<irq_flipper_bottom_line)&0xf8;
|
||||
*HARDWARE_IRQ = &irq_flipper_bottom;
|
||||
// Acknowledge the IRQ
|
||||
*IRQ_STATUS = IRQ_RASTER;
|
||||
}
|
||||
|
||||
// Middle of the flipper
|
||||
volatile unsigned int irq_flipper_idx = 0x00;
|
||||
|
||||
// IRQ running during set-up
|
||||
// Flips from start screen to bitmap (starts the start-up screen)
|
||||
__interrupt void irq_flipper_bottom() {
|
||||
asm { nop nop nop nop nop nop nop nop nop nop nop nop nop nop nop nop nop nop nop nop nop nop nop }
|
||||
raster_fine((<irq_flipper_bottom_line)&7);
|
||||
// Colors
|
||||
asm { nop nop nop nop }
|
||||
VICII->BORDER_COLOR = LIGHT_BLUE;
|
||||
VICII->BG_COLOR = BLUE;
|
||||
// Show default screen
|
||||
VICII->MEMORY = toD018(LOAD_SCREEN, LOAD_CHARSET);
|
||||
|
||||
if(!flipper_done) {
|
||||
// Move the flipper down
|
||||
unsigned int irq_flipper_line = FLIPPER_EASING[irq_flipper_idx++];
|
||||
// Check limits
|
||||
if(irq_flipper_line<8)
|
||||
irq_flipper_top_line = 0;
|
||||
else
|
||||
irq_flipper_top_line = irq_flipper_line-8;
|
||||
|
||||
if(irq_flipper_line>0x128)
|
||||
irq_flipper_bottom_line = 0x130;
|
||||
else
|
||||
irq_flipper_bottom_line = irq_flipper_line+8;
|
||||
|
||||
// Are we done
|
||||
if(irq_flipper_line==0x130)
|
||||
flipper_done = 1;
|
||||
}
|
||||
|
||||
// Set up the IRQ again
|
||||
*VICII_CONTROL |=0x80;
|
||||
*RASTER = IRQ_PART1_TOP_LINE;
|
||||
*HARDWARE_IRQ = &irq_part1_top;
|
||||
// Acknowledge the IRQ
|
||||
*IRQ_STATUS = IRQ_RASTER;
|
||||
}
|
||||
|
||||
// Waits until at the exact start of raster line
|
||||
// Excepts to start at a line divisible by 8 (0x00, 0x08, x010, ...).
|
||||
// Waits line_offset (0-7) additional lines.
|
||||
void raster_fine(char line_offset) {
|
||||
kickasm(uses line_offset, uses RASTER_BADLINES, clobbers "AXY") {{
|
||||
jmp aligned
|
||||
.align $100
|
||||
aligned:
|
||||
ldy RASTER
|
||||
ldx line_offset
|
||||
inx
|
||||
rst:
|
||||
nop
|
||||
nop
|
||||
nop
|
||||
nop
|
||||
dex // 2
|
||||
beq done // 2
|
||||
lda RASTER_BADLINES,y // 4
|
||||
beq notbad // 3
|
||||
bad:
|
||||
nop // 2
|
||||
nop
|
||||
nop
|
||||
nop
|
||||
nop
|
||||
dex
|
||||
beq done
|
||||
iny
|
||||
nop
|
||||
bit $ea
|
||||
notbad:
|
||||
.fill 18, NOP
|
||||
bit $ea
|
||||
iny
|
||||
jmp rst
|
||||
done:
|
||||
}}
|
||||
}
|
||||
|
||||
// The current char line where the flipper switches from bitmap to text
|
||||
volatile char flipper_charline = 0;
|
||||
|
||||
// Fixes the colors for the flipper
|
||||
// Updates with bitmap colors when the bitmap is being shown
|
||||
void flipper_fix_colors() {
|
||||
if(irq_flipper_top_line>0x2e && irq_flipper_top_line<0xf6) {
|
||||
char charline = (char)((irq_flipper_top_line-0x2e)/8);
|
||||
if(charline>=flipper_charline) {
|
||||
unsigned int offset = (unsigned int)flipper_charline*40;
|
||||
// We have to update some colors
|
||||
char* colors = COLS+offset;
|
||||
char* happy_cols = P1_COLORS+offset;
|
||||
for(char i=0;i<40;i++)
|
||||
colors[i] = happy_cols[i];
|
||||
flipper_charline++;
|
||||
}
|
||||
}
|
||||
}
|
@ -0,0 +1,631 @@
|
||||
// 2-screen bitmap logo and a multiplexer scroller
|
||||
|
||||
#include <c64.h>
|
||||
#include <6502.h>
|
||||
#include <string.h>
|
||||
#include "demo.h"
|
||||
#include "byteboozer.h"
|
||||
|
||||
#pragma code_seg(CodePart2)
|
||||
#pragma data_seg(DataPart2)
|
||||
|
||||
#include "../clib/vsp.h"
|
||||
#define PLEX_SPRITE_PTRS 0xe3f8
|
||||
#include "multiplex-bucket.h"
|
||||
|
||||
// Memory layout of the graphics bank
|
||||
char * const LOGO_DATA = 0x5400;
|
||||
char * const PART2_BITMAP = 0xc000; // -0xdfff
|
||||
char * const PART2_SCREEN = 0xe000; // -0xe400
|
||||
char * const PART2_SPRITES = 0xe400; // -0xf400
|
||||
// Location PLEX ID updaters are placed when running
|
||||
char * const PLEX_ID_UPDATERS = 0x3c00;
|
||||
// Location where the crunched PLEX ID updaters are placed to be decrunched
|
||||
char * const PLEX_ID_UPDATERS_CRUNCHED2 = 0x7c00; // -0xFF72
|
||||
// Size of the crunched PLEX ID updaters
|
||||
const unsigned int PLEX_ID_UPDATERS_CRUNCHED_SIZE = 0x0b72;
|
||||
// Location where the crunched LOGO DATA is placed to be decrunched
|
||||
char * const LOGO_DATA_CRUNCHED2 = 0x8800; // -0xAA2D
|
||||
// Size of the crunched PLEX ID updaters
|
||||
const unsigned int LOGO_DATA_CRUNCHED_SIZE = 0x222d;
|
||||
|
||||
// Char-based sizes for the logo
|
||||
const char LOGO_HEIGHT = 25;
|
||||
const char LOGO_WIDTH = 80;
|
||||
|
||||
// Address of screen data
|
||||
char * const LOGO_DATA_SCREEN = LOGO_DATA;
|
||||
// Address of color data
|
||||
char * const LOGO_DATA_COLORS = LOGO_DATA_SCREEN+LOGO_HEIGHT*LOGO_WIDTH;
|
||||
// Address of pixel data
|
||||
char * const LOGO_DATA_BITMAP = LOGO_DATA_COLORS+LOGO_HEIGHT*LOGO_WIDTH;
|
||||
|
||||
// Sprite pointer for sprite 0
|
||||
#define SPRITE_0 toSpritePtr(PART2_SPRITES)
|
||||
|
||||
// The sprite font
|
||||
#pragma data_seg(InitPart2)
|
||||
char SPRITES_CRUNCHED[] = kickasm(resource "spritefont.png") {{
|
||||
.modify B2() {
|
||||
.pc = PART2_SPRITES "PART2_SPRITES"
|
||||
.var p2_sprites = LoadPicture("spritefont.png", List().add($000000, $ffffff))
|
||||
.for(var sy=0;sy<8;sy++) {
|
||||
.for(var sx=0;sx<8;sx++) {
|
||||
.for (var y=0;y<21; y++) {
|
||||
.for (var c=0; c<3; c++) {
|
||||
.byte p2_sprites.getSinglecolorByte(sx*3+c,sy*21+y)
|
||||
}
|
||||
}
|
||||
.byte 0
|
||||
}
|
||||
}
|
||||
}
|
||||
}};
|
||||
#pragma data_seg(DataPart2)
|
||||
|
||||
#pragma data_seg(InitPart2)
|
||||
char LOGO_DATA_CRUNCHED[] = kickasm(resource "logo-bitmap-640.png", resource "mcbitmap.asm", uses LOGO_HEIGHT, uses LOGO_WIDTH) {{
|
||||
.modify B2() {
|
||||
.pc = LOGO_DATA "LOGO DATA"
|
||||
#import "mcbitmap.asm"
|
||||
.var mcBmmData2 = getMcBitmapData(LoadPicture("logo-bitmap-640.png"))
|
||||
// Screen data
|
||||
.for (var y=0; y<LOGO_HEIGHT; y++)
|
||||
.for (var x=0; x<LOGO_WIDTH; x++)
|
||||
.byte getMcScreenData(x, y, mcBmmData2)
|
||||
// Color Data
|
||||
.for (var y=0; y<LOGO_HEIGHT; y++)
|
||||
.for (var x=0; x<LOGO_WIDTH; x++)
|
||||
.byte getMcColorData(x, y, mcBmmData2)
|
||||
// Bitmap Data (row by row)
|
||||
.for (var y=0; y<LOGO_HEIGHT; y++)
|
||||
.for (var i=0; i<8; i++)
|
||||
.for (var x=0; x<LOGO_WIDTH; x++)
|
||||
.byte getMcPixelData(x, y, i, mcBmmData2)
|
||||
}
|
||||
}};
|
||||
#pragma data_seg(DataPart2)
|
||||
|
||||
// A sinus table with values [0;320]
|
||||
__align(0x100) unsigned int VSP_SINTABLE[0x100] = kickasm {{
|
||||
.fillword $100, round(160+160*sin(2*PI*i/256))
|
||||
}};
|
||||
|
||||
// The sequence of colors for the sprites
|
||||
char SPRITE_COLOR_SEQUENCE[] = {
|
||||
WHITE, WHITE, WHITE, WHITE, WHITE, WHITE, WHITE, WHITE, WHITE, WHITE,
|
||||
WHITE, WHITE, WHITE, WHITE, WHITE, WHITE, WHITE, WHITE, WHITE, WHITE, WHITE, WHITE,
|
||||
WHITE, WHITE, WHITE, WHITE, WHITE, WHITE, WHITE, WHITE, WHITE, WHITE, WHITE, WHITE,
|
||||
WHITE, YELLOW, CYAN, GREEN, PURPLE, RED, BLUE, RED, PURPLE, GREEN, CYAN, YELLOW,
|
||||
WHITE, WHITE,
|
||||
};
|
||||
|
||||
__align(0x100) char SCROLL_YSIN[0x100] = kickasm {{
|
||||
.fill $100, round(139+89.5*sin(toRadians(360*i/256)))
|
||||
}};
|
||||
|
||||
// The buckets containing sprites to show. 8 sprites in each bucket.
|
||||
__align(0x100) struct BucketSprite BUCKET_SPRITES[FRAME_COUNT*BUCKET_COUNT*BUCKET_SIZE];
|
||||
|
||||
// Copy of the original buckets containing sprites to show. 8 sprites in each bucket. (Used for adding the plex_id_offset)
|
||||
__align(0x100) struct BucketSprite ORIGINAL_BUCKET_SPRITES[FRAME_COUNT*BUCKET_COUNT*BUCKET_SIZE];
|
||||
|
||||
__align(0x100) char XMOVEMENT[0x400] = kickasm {{
|
||||
//.lohifill $100, round(344-i*344/$100-86*sin(toRadians(360*i/$100)))
|
||||
.lohifill $200, round(344-i*344/$100-129*sin(toRadians(360*i/$100)))
|
||||
//.lohifill $100, round(344-i*344/$100-86*sin(toRadians(360*i/$80)))
|
||||
//.lohifill $100, round(344-i*344/$100 -86*sin(toRadians(360*i/$100)) -43*sin(toRadians(360*i/$80)))
|
||||
//.lohifill $100, 344-i*344/$100
|
||||
}};
|
||||
|
||||
// The high-value table
|
||||
char * const XMOVEMENT_HI = XMOVEMENT+0x200;
|
||||
|
||||
// The scroll text
|
||||
char SCROLL_TEXT[] =
|
||||
"\x2a "
|
||||
"most people will remember 2020 for a long time. "
|
||||
"for us nerds, it was a chance to dig deep into our hobbies. "
|
||||
"but we do miss the demoparties, and drinking beers with you crazy people... "
|
||||
"it is the 30th birthday of camelot, and this virtual greeting card is our way of celebrating with all of you! "
|
||||
" credits "
|
||||
"\x2a code: rex "
|
||||
"\x2a music: linus "
|
||||
"\x2a graphics: bizkid, snabel & vic "
|
||||
"\x2a "
|
||||
"camelot sends love to "
|
||||
"\xff abyss connection "
|
||||
"\xff algotech "
|
||||
"\xff ancients "
|
||||
"\xff arsenic "
|
||||
"\xff arise "
|
||||
"\xff artline designs "
|
||||
"\xff artstate "
|
||||
"\xff atlantis "
|
||||
"\xff bonzai "
|
||||
"\xff booze design "
|
||||
"\xff censor design "
|
||||
"\xff cosine "
|
||||
"\xff crest "
|
||||
"\xff chorus "
|
||||
"\xff dekadence "
|
||||
"\xff delysid "
|
||||
"\xff desire "
|
||||
"\xff elysium "
|
||||
"\xff excess "
|
||||
"\xff extend "
|
||||
"\xff faic "
|
||||
"\xff f4cg "
|
||||
"\xff fairlight "
|
||||
"\xff fossil "
|
||||
"\xff glance "
|
||||
"\xff genesis project "
|
||||
"\xff haujobb "
|
||||
"\xff hitmen "
|
||||
"\xff hoaxers "
|
||||
"\xff hokuto force "
|
||||
"\xff horizon "
|
||||
"\xff illusion "
|
||||
"\xff john dillermand "
|
||||
"\xff laxity "
|
||||
"\xff lepsi de "
|
||||
"\xff lethargy "
|
||||
"\xff mayday "
|
||||
"\xff megastyle "
|
||||
"\xff multistyle labs "
|
||||
"\xff nah-kolor "
|
||||
"\xff noice "
|
||||
"\xff offence "
|
||||
"\xff onslaught "
|
||||
"\xff oxyron "
|
||||
"\xff padua "
|
||||
"\xff panda design "
|
||||
"\xff panoramic designs "
|
||||
"\xff performers "
|
||||
"\xff plush "
|
||||
"\xff pretzel logic "
|
||||
"\xff prosonix "
|
||||
"\xff proxima "
|
||||
"\xff rabenauge "
|
||||
"\xff radwar "
|
||||
"\xff rebels "
|
||||
"\xff resource "
|
||||
"\xff samar "
|
||||
"\xff scenesat "
|
||||
"\xff shape "
|
||||
"\xff siesta "
|
||||
"\xff silicon ltd. "
|
||||
"\xff singular "
|
||||
"\xff software of sweden "
|
||||
"\xff starion "
|
||||
"\xff success "
|
||||
"\xff svenonacid "
|
||||
"\xff the dreams "
|
||||
"\xff the solution "
|
||||
"\xff triad "
|
||||
"\xff tropyx "
|
||||
"\xff trsi "
|
||||
"\xff unicess "
|
||||
"\xff up rough "
|
||||
"\xff vision "
|
||||
"\xff xenon "
|
||||
"\xff xentax "
|
||||
"\xff "
|
||||
"... we hope to see you all again in 2021..."
|
||||
" "
|
||||
;
|
||||
|
||||
// IRQ performing the VSP
|
||||
const char IRQ_SWING_VSP_LINE = 0x2d;
|
||||
|
||||
void part2_init() {
|
||||
// Decrunch sprites
|
||||
byteboozer_decrunch(SPRITES_CRUNCHED);
|
||||
// Move the crunched logo data out of harms way
|
||||
memcpy(LOGO_DATA_CRUNCHED2, LOGO_DATA_CRUNCHED, LOGO_DATA_CRUNCHED_SIZE);
|
||||
// Move the crunched plex updaters out of harms way
|
||||
memcpy(PLEX_ID_UPDATERS_CRUNCHED2, PLEX_ID_UPDATERS_CRUNCHED, PLEX_ID_UPDATERS_CRUNCHED_SIZE);
|
||||
// Decrunch multiplexer frame updaters (from new location)
|
||||
byteboozer_decrunch(PLEX_ID_UPDATERS_CRUNCHED2);
|
||||
// Decrunch logo data
|
||||
byteboozer_decrunch(LOGO_DATA_CRUNCHED2);
|
||||
|
||||
// Empty the hidden part of the bitmap
|
||||
*PROCPORT_DDR = PROCPORT_DDR_MEMORY_MASK;
|
||||
*PROCPORT = PROCPORT_RAM_ALL;
|
||||
memset(PART2_BITMAP+8000, 0, 192);
|
||||
*PROCPORT_DDR = PROCPORT_DDR_MEMORY_MASK;
|
||||
*PROCPORT = PROCPORT_RAM_IO;
|
||||
|
||||
// Empty screen & cols
|
||||
memset(COLS, BLACK, 1024);
|
||||
memset(PART2_SCREEN, BLACK, 1000);
|
||||
|
||||
// Fade the sparkler
|
||||
VICII->SPRITE0_COLOR = GREY;
|
||||
VICII->SPRITES_MCOLOR1 = BROWN;
|
||||
VICII->SPRITES_MCOLOR2 = BLUE;
|
||||
|
||||
// Initialize PLEX tables
|
||||
plexPrepareInit();
|
||||
// Prepare 8 frames of y-positions into BUCKET_SPRITES
|
||||
struct BucketSprite* frame = BUCKET_SPRITES;
|
||||
for(char frame_idx=0;frame_idx<8;frame_idx++) {
|
||||
char sin_idx = frame_idx;
|
||||
for(char s=0; s<PLEX_COUNT;s++) {
|
||||
PLEX_YPOS[s] = SCROLL_YSIN[sin_idx];
|
||||
sin_idx += 8;
|
||||
}
|
||||
// Perform bucket sort
|
||||
plexPrepareFrame(frame);
|
||||
// Move to Next frame
|
||||
frame += BUCKET_SIZE*BUCKET_COUNT;
|
||||
}
|
||||
// Copy the original buckets
|
||||
memcpy(ORIGINAL_BUCKET_SPRITES, BUCKET_SPRITES, sizeof(BUCKET_SPRITES));
|
||||
// Set the initial sprite pointers
|
||||
for(char s=0;s<PLEX_COUNT;s++) {
|
||||
PLEX_PTR[s] = SPRITE_0+' ';
|
||||
}
|
||||
// Disable sparkler
|
||||
VICII->SPRITES_XMSB = 0x00;
|
||||
VICII->SPRITE0_X = 0x00;
|
||||
VICII->SPRITES_ENABLE = 0x00;
|
||||
// Set sprite colors
|
||||
VICII->SPRITES_MC = 0x00;
|
||||
for(char s=0;s<8;s++)
|
||||
SPRITES_COLOR[s] = WHITE;
|
||||
|
||||
// Empty the rest of the screen
|
||||
memset(PART2_SCREEN+1000, BLACK, 24);
|
||||
|
||||
}
|
||||
|
||||
void part2_run() {
|
||||
SEI();
|
||||
// Colors
|
||||
VICII->BORDER_COLOR = BLACK;
|
||||
VICII->BG_COLOR = BLACK;
|
||||
// Change graphics bank
|
||||
CIA2->PORT_A = toDd00(PART2_SCREEN);
|
||||
// Show screen
|
||||
VICII->MEMORY = toD018(PART2_SCREEN, PART2_BITMAP);
|
||||
// Set bitmap mode
|
||||
VICII->CONTROL1 |= VICII_BMM;
|
||||
// Enable & initialize sprites
|
||||
*SPRITES_ENABLE = 0xff;
|
||||
// Disable CIA 1 Timer IRQ
|
||||
CIA1->INTERRUPT = CIA_INTERRUPT_CLEAR;
|
||||
// Acknowledge any timer IRQ
|
||||
asm { lda CIA1_INTERRUPT }
|
||||
// Acknowledge any VIC IRQ
|
||||
*IRQ_STATUS = 0x0f;
|
||||
// Set raster line to first bucket
|
||||
*VICII_CONTROL &=0x7f;
|
||||
*RASTER = BUCKET_YPOS[0];
|
||||
// Enable Raster Interrupt
|
||||
*IRQ_ENABLE = IRQ_RASTER;
|
||||
// Set the IRQ routine
|
||||
*HARDWARE_IRQ = &irq_swing_top;
|
||||
// Enable the IRQ again
|
||||
CLI();
|
||||
|
||||
// The current frame ID (0-7)
|
||||
plex_frame_id = 0;
|
||||
// Pointer to the buckets of the current frame
|
||||
plex_frame = BUCKET_SPRITES;
|
||||
// Offset added to plex_id to ensure the sprite cycling works (decreased 1 every time a cycle is complete)
|
||||
plex_id_offset = 0;
|
||||
|
||||
part2_loop();
|
||||
|
||||
}
|
||||
|
||||
// Part 2 main loop
|
||||
void part2_loop() {
|
||||
p2_work_ready = 0;
|
||||
for(;;) {
|
||||
while(p2_work_ready==0) ;
|
||||
// Play music
|
||||
demo_work();
|
||||
// Reveal logo
|
||||
if(p2_logo_revealing && !p2_logo_reveal_done)
|
||||
p2_logo_reveal();
|
||||
// Show logo at 0:18,50
|
||||
if(!p2_logo_revealing && demo_frame_count>18*50+25)
|
||||
p2_logo_revealing = 1;
|
||||
// Move logo as soon as reveal is done (40 frames) 0:19,30
|
||||
if(!p2_logo_swinging && p2_logo_reveal_done)
|
||||
p2_logo_swinging = 1;
|
||||
// Start plex scroller at 0:26
|
||||
if(!p2_plex_scroller_moving && demo_frame_count>26*50)
|
||||
p2_plex_scroller_moving = 1;
|
||||
// My work is done!
|
||||
p2_work_ready = 0;
|
||||
}
|
||||
}
|
||||
|
||||
// Signals the main() loop to do work when all rasters are complete
|
||||
volatile char p2_work_ready;
|
||||
|
||||
// 1 if the logo is being revealed
|
||||
volatile char p2_logo_revealing = 0;
|
||||
|
||||
// 1 if the logo is completely revealed
|
||||
volatile char p2_logo_reveal_done = 0;
|
||||
|
||||
// 1 if the logo is being showed
|
||||
volatile char p2_logo_swinging = 0;
|
||||
|
||||
// 1 if the scroll is moving
|
||||
volatile char p2_plex_scroller_moving = 0;
|
||||
|
||||
// Number of columns shown of the logo
|
||||
volatile char p2_logo_reveal_idx = 0;
|
||||
|
||||
// Reveals the logo column by column
|
||||
void p2_logo_reveal() {
|
||||
if(p2_logo_reveal_idx>=40)
|
||||
p2_logo_reveal_done = 1;
|
||||
else
|
||||
vsp_update_screen(p2_logo_reveal_idx++);
|
||||
}
|
||||
|
||||
// X-movement index
|
||||
volatile char x_movement_idx = 0;
|
||||
// The next char to use from the scroll text
|
||||
char* volatile scroll_text_next = SCROLL_TEXT;
|
||||
|
||||
// Scroll the plex sprites to the left.
|
||||
void plex_scroller_move() {
|
||||
char x_idx = x_movement_idx;
|
||||
for(char s=0; s<PLEX_COUNT;s++) {
|
||||
PLEX_XPOS[s] = XMOVEMENT[x_idx];
|
||||
PLEX_XPOS_MSB[s] = XMOVEMENT_HI[x_idx];
|
||||
if(x_idx==0) {
|
||||
// Page boundary crossed - new scroll char! Detection is a bit flaky!
|
||||
// Restart scroll text of needed
|
||||
if(*scroll_text_next==0x00)
|
||||
scroll_text_next = SCROLL_TEXT;
|
||||
// Read next char from the scroll text
|
||||
char letter = *scroll_text_next++;
|
||||
// If the letter is \xff then add a heart
|
||||
if(letter==0xff)
|
||||
letter = 0x00;
|
||||
// Add the letter
|
||||
PLEX_PTR[s] = SPRITE_0+letter;
|
||||
}
|
||||
x_idx +=8;
|
||||
}
|
||||
x_movement_idx++;
|
||||
}
|
||||
|
||||
// The current frame ID (0-7)
|
||||
volatile char plex_frame_id = 0;
|
||||
// Pointer to the buckets of the current frame
|
||||
struct BucketSprite* volatile plex_frame = BUCKET_SPRITES;
|
||||
// Offset added to plex_id to ensure the sprite cycling works (decreased 1 every time a cycle is complete)
|
||||
volatile char plex_id_offset = 0;
|
||||
// Pointer to the current bucket of the current frame
|
||||
struct BucketSprite* volatile plex_bucket = BUCKET_SPRITES;
|
||||
// Index of the current bucket in the current frame (0..BUCKET_COUNT-1)
|
||||
volatile char plex_bucket_id = 0;
|
||||
|
||||
// Inititialize plex frame and show first bucket
|
||||
__interrupt void irq_swing_top() {
|
||||
//*BORDER_COLOR = DARK_GREY;
|
||||
//VICII->BORDER_COLOR++;
|
||||
// Initialize the multiplexer frame
|
||||
plexFrameStart();
|
||||
plex_bucket = plex_frame;
|
||||
plex_bucket_id = 0;
|
||||
// Show the first bucket
|
||||
plexBucketShow(plex_bucket);
|
||||
// Move forward to the next bucket
|
||||
plex_bucket += BUCKET_SIZE;
|
||||
plex_bucket_id++;
|
||||
// Acknowledge the IRQ
|
||||
*IRQ_STATUS = IRQ_RASTER;
|
||||
// Set up the VSP IRQ
|
||||
*HARDWARE_IRQ = &irq_swing_vsp;
|
||||
*RASTER = IRQ_SWING_VSP_LINE;
|
||||
//*BORDER_COLOR = BLACK;
|
||||
//VICII->BORDER_COLOR--;
|
||||
}
|
||||
|
||||
// The fine scroll (0-7)
|
||||
volatile char vsp_fine_scroll;
|
||||
// The coarse scroll (0-40)
|
||||
volatile char vsp_coarse_scroll;
|
||||
|
||||
// Show sprites from the multiplexer, rescheduling the IRQ as many times as needed
|
||||
__interrupt void irq_swing_vsp() {
|
||||
// Perform VSP scrolling of the screen (must be the first call in the IRQ)
|
||||
vsp_perform();
|
||||
// Set BMM
|
||||
VICII->CONTROL1 |= VICII_BMM;
|
||||
// Set fine scroll (and MCM)
|
||||
VICII->CONTROL2 = vsp_fine_scroll | VICII_MCM;
|
||||
|
||||
//*BORDER_COLOR = DARK_GREY;
|
||||
// Acknowledge the IRQ
|
||||
*IRQ_STATUS = IRQ_RASTER;
|
||||
// Set up the PLEX IRQ (handles the rest of the multiplexer buckets)
|
||||
*HARDWARE_IRQ = &irq_swing_plex;
|
||||
*RASTER = BUCKET_YPOS[1];
|
||||
//*BORDER_COLOR = BLACK;
|
||||
}
|
||||
|
||||
// Index into the VSP sinus value
|
||||
volatile char vsp_sin_idx = 0x40;
|
||||
|
||||
// Index into the sprite color sequence
|
||||
volatile char sprite_color_idx = 0;
|
||||
|
||||
// Show sprites from the multiplexer, rescheduling the IRQ for each bucket
|
||||
__interrupt void irq_swing_plex() {
|
||||
//*BORDER_COLOR = DARK_GREY;
|
||||
// Show the bucket
|
||||
plexBucketShow(plex_bucket);
|
||||
// Move forward to the next bucket
|
||||
plex_bucket += BUCKET_SIZE;
|
||||
plex_bucket_id++;
|
||||
if(plex_bucket_id<BUCKET_COUNT) {
|
||||
// Not done with the frame yet - set up the next PLEX IRQ (handles the rest of the multiplexer buckets)
|
||||
*HARDWARE_IRQ = &irq_swing_plex;
|
||||
*RASTER = BUCKET_YPOS[plex_bucket_id];
|
||||
//*BORDER_COLOR = BLACK;
|
||||
} else {
|
||||
// We are done with this frame - finish it and perform other stuff!
|
||||
//VICII->BORDER_COLOR = RED;
|
||||
// Set up the TOP IRQ
|
||||
*HARDWARE_IRQ = &irq_swing_top;
|
||||
*RASTER = BUCKET_YPOS[0];
|
||||
|
||||
// Move to the next frame of the plexer
|
||||
const char YMOVE = 3;
|
||||
plex_frame_id += YMOVE;
|
||||
plex_frame += (unsigned int)YMOVE*BUCKET_COUNT*BUCKET_SIZE;
|
||||
if(plex_frame_id>=FRAME_COUNT) {
|
||||
// Reset to start of cycle
|
||||
plex_frame -= BUCKET_COUNT*BUCKET_SIZE*FRAME_COUNT;
|
||||
plex_frame_id -= FRAME_COUNT;
|
||||
// And change the PLEX ID offset
|
||||
plex_id_offset--;
|
||||
}
|
||||
// Update plex_id in the next frame
|
||||
//VICII->BORDER_COLOR = BLUE;
|
||||
update_frame_plex_id_offset(plex_frame_id);
|
||||
|
||||
if(p2_logo_swinging) {
|
||||
// Update the VSP value with a sinus
|
||||
unsigned int scroll = VSP_SINTABLE[(unsigned int)(vsp_sin_idx++)];
|
||||
vsp_fine_scroll = (char)scroll&7;
|
||||
char new_coarse_scroll = (char)(scroll/8);
|
||||
// Update the VSP value with a sinus
|
||||
char coarse_scroll_diff = vsp_coarse_scroll - new_coarse_scroll;
|
||||
// Update screen column (if needed)
|
||||
if(coarse_scroll_diff==0x01) {
|
||||
// Moving left - put a new column at the right border
|
||||
char x_offset = 0x50-vsp_coarse_scroll;
|
||||
// Only move 24 - because the last line is empty (and holds sprite pointers)
|
||||
vsp_update_screen(x_offset);
|
||||
} else if(coarse_scroll_diff==0xff) {
|
||||
// Moving right - put a new column at the left border
|
||||
char x_offset = 0x27-vsp_coarse_scroll;
|
||||
vsp_update_screen(x_offset);
|
||||
// Clear line 25 - because the start of the last line was over-written by line #24 chars 40-80
|
||||
(PART2_SCREEN+24*40)[x_offset] = 0; //(LOGO_DATA_SCREEN+24*80)[x_offset];
|
||||
(COLS+24*40)[x_offset] = 0; //(LOGO_DATA_COLORS+24*80)[x_offset];
|
||||
}
|
||||
vsp_coarse_scroll = new_coarse_scroll;
|
||||
vsp_scroll = 40-vsp_coarse_scroll;
|
||||
}
|
||||
|
||||
// Move the sprites (X-position & new scroll chars)
|
||||
if(p2_plex_scroller_moving) {
|
||||
plex_scroller_move();
|
||||
// Change sprite colors
|
||||
if(++sprite_color_idx == sizeof(SPRITE_COLOR_SEQUENCE)) sprite_color_idx = 0;
|
||||
for(char s=0;s<8;s++)
|
||||
SPRITES_COLOR[s] = SPRITE_COLOR_SEQUENCE[sprite_color_idx];
|
||||
}
|
||||
|
||||
// Signal the main routine
|
||||
p2_work_ready = 1;
|
||||
|
||||
}
|
||||
// Acknowledge the IRQ
|
||||
*IRQ_STATUS = IRQ_RASTER;
|
||||
}
|
||||
|
||||
// Update the plex_id's of a multiplexer frame to reflect a specific plex_id_offset
|
||||
void update_frame_plex_id_offset(char plex_frame_id) {
|
||||
unsigned int* jmp_table = (unsigned int*)PLEX_ID_UPDATERS;
|
||||
unsigned int jmp_address = jmp_table[plex_frame_id];
|
||||
kickasm(uses jmp_address) {{
|
||||
lda jmp_address
|
||||
sta call+1
|
||||
lda jmp_address+1
|
||||
sta call+2
|
||||
call: jsr $0000
|
||||
}}
|
||||
}
|
||||
|
||||
// Unrolled ASM to update plex_id_offset for FRAMES 0-7
|
||||
#pragma data_seg(InitPart2)
|
||||
char PLEX_ID_UPDATERS_CRUNCHED[] = kickasm(uses ORIGINAL_BUCKET_SPRITES, uses BUCKET_SPRITES, uses plex_id_offset) {{
|
||||
.modify B2() {
|
||||
.pc = PLEX_ID_UPDATERS "PLEX_ID_UPDATERS"
|
||||
// First generate a jump table
|
||||
.for(var frame=0;frame<8;frame++)
|
||||
.word updaters[frame].updater
|
||||
// Generate the 8 unrolled updaters
|
||||
updaters:
|
||||
.for(var frame=0;frame<8;frame++) {
|
||||
updater:
|
||||
ldx #$1f
|
||||
.for(var sprite=0; sprite<9*8; sprite++ ) {
|
||||
lda ORIGINAL_BUCKET_SPRITES + frame*8*9*2 + sprite*2 +1
|
||||
clc
|
||||
adc plex_id_offset
|
||||
sax BUCKET_SPRITES + frame*8*9*2 + sprite*2 +1
|
||||
}
|
||||
rts
|
||||
}
|
||||
}
|
||||
}};
|
||||
#pragma data_seg(DataPart2)
|
||||
|
||||
// Update screen, colors and bitmap with a single column of new data
|
||||
// - x_offset is the offset of the column to update (0-79)
|
||||
void vsp_update_screen(__ma char x_offset) {
|
||||
// Update screen and colors
|
||||
kickasm(uses x_offset, uses PART2_SCREEN, uses COLS,uses LOGO_DATA, uses LOGO_DATA_COLORS, clobbers "AX") {{
|
||||
ldx x_offset
|
||||
.for(var row=0;row<24;row++) {
|
||||
lda LOGO_DATA+80*row,x
|
||||
sta PART2_SCREEN+40*row,x
|
||||
lda LOGO_DATA_COLORS+80*row,x
|
||||
sta COLS+40*row,x
|
||||
}
|
||||
}}
|
||||
// Disable I/O (BITMAP is below I/O)
|
||||
*PROCPORT_DDR = PROCPORT_DDR_MEMORY_MASK;
|
||||
*PROCPORT = PROCPORT_RAM_ALL;
|
||||
// Update bitmap (using 3 routines to handle all bitmap columns)
|
||||
unsigned int x_offset8 = (unsigned int)x_offset*8;
|
||||
if(>x_offset8 == 0) {
|
||||
kickasm(uses x_offset, uses x_offset8, uses PART2_BITMAP, uses LOGO_DATA_BITMAP, clobbers "AXY") {{
|
||||
ldx x_offset
|
||||
ldy x_offset8
|
||||
.for(var row=0;row<24;row++)
|
||||
.for(var pix=0;pix<8;pix++) {
|
||||
lda LOGO_DATA_BITMAP+80*(row*8+pix),x
|
||||
sta PART2_BITMAP+row*40*8+pix,y
|
||||
}
|
||||
}}
|
||||
} else if(>x_offset8 == 1) {
|
||||
kickasm(uses x_offset, uses x_offset8, uses PART2_BITMAP, uses LOGO_DATA_BITMAP, clobbers "AXY") {{
|
||||
ldx x_offset
|
||||
ldy x_offset8
|
||||
.for(var row=0;row<24;row++)
|
||||
.for(var pix=0;pix<8;pix++) {
|
||||
lda LOGO_DATA_BITMAP+80*(row*8+pix),x
|
||||
sta PART2_BITMAP+$100+row*40*8+pix,y
|
||||
}
|
||||
}}
|
||||
} else { // >x_offset8 == 2
|
||||
kickasm(uses x_offset, uses x_offset8, uses PART2_BITMAP, uses LOGO_DATA_BITMAP, clobbers "AXY") {{
|
||||
ldx x_offset
|
||||
ldy x_offset8
|
||||
.for(var row=0;row<24;row++)
|
||||
.for(var pix=0;pix<8;pix++) {
|
||||
lda LOGO_DATA_BITMAP+80*(row*8+pix),x
|
||||
sta PART2_BITMAP+$200+row*40*8+pix,y
|
||||
}
|
||||
}}
|
||||
}
|
||||
*PROCPORT_DDR = PROCPORT_DDR_MEMORY_MASK;
|
||||
*PROCPORT = PROCPORT_RAM_IO;
|
||||
}
|
BIN
src/test/kc/complex/new_30_years_low_resolution/sparklers.png
Normal file
BIN
src/test/kc/complex/new_30_years_low_resolution/sparklers.png
Normal file
Binary file not shown.
After Width: | Height: | Size: 1.6 KiB |
BIN
src/test/kc/complex/new_30_years_low_resolution/spritefont.png
Normal file
BIN
src/test/kc/complex/new_30_years_low_resolution/spritefont.png
Normal file
Binary file not shown.
After Width: | Height: | Size: 4.2 KiB |
Loading…
Reference in New Issue
Block a user