oscar64/samples/scrolling/cgrid8way.c

436 lines
7.9 KiB
C

#include <c64/vic.h>
#include <c64/memmap.h>
#include <string.h>
#include <c64/joystick.h>
#include <stdlib.h>
// Screen and color space
#define Screen ((byte *)0x0400)
#define Color ((byte *)0xd800)
// Macro for easy access to screen and color space
#define sline(x, y) (Screen + 40 * (y) + (x))
#define cline(x, y) (Color + 40 * (y) + (x))
// Tile data, each column has four rows of four tiles
static const char quad[4][4 * 4] =
{
{
0x20, 0x55, 0x6c, 0x4e,
0x20, 0x5d, 0xe1, 0x65,
0x20, 0x5d, 0xe1, 0x65,
0x20, 0x4a, 0x7c, 0x4d,
},
{
0x20, 0x40, 0x62, 0x77,
0x20, 0x20, 0xa0, 0x20,
0x20, 0x20, 0xa0, 0x20,
0x20, 0x40, 0xe2, 0x6f,
},
{
0x20, 0x40, 0x62, 0x77,
0x20, 0x20, 0xa0, 0x20,
0x20, 0x20, 0xa0, 0x20,
0x20, 0x40, 0xe2, 0x6f,
},
{
0x20, 0x49, 0x7b, 0x4d,
0x20, 0x5d, 0x61, 0x6a,
0x20, 0x5d, 0x61, 0x6a,
0x20, 0x4b, 0x7e, 0x4e,
}
};
#pragma align(quad, 256)
// Expand a single row into an offscreen buffer
void expandrow(char * dp, char * cp, const char * grid, char ly, char lx)
{
char hx = 0;
for(char x=0; x<40; x++)
{
dp[x] = quad[lx][ly + grid[hx]];
cp[x] = grid[hx];
lx++;
if (lx == 4)
{
lx = 0;
hx++;
}
}
}
// Expand a single column into an offscreen buffer
void expandcol(char * dp, char * cp, const char * grid, char ly, char lx)
{
for(char y=0; y<25; y++)
{
dp[y] = quad[lx][ly + grid[0]];
cp[y] = grid[0];
ly += 4;
if (ly == 16)
{
grid += 16;
ly = 0;
}
}
}
// Two split scroll for left, right and up
#define VSPLIT 12
// Three split scroll for down. Downscrolling is more tricky because
// we have to copy towards the raster
#define VSPLIT 12
#define VSPLIT2 20
// New line/column of screen and color data
char news[40], newc[40];
// All scroll routines start with a pixel offset of 4, 4
// Scroll one character left in two pixel increments
void scroll_left(void)
{
// Wait for one frame
vic_waitTop();
vic_waitBottom();
// Switch to offset 2, 4
vic.ctrl2 = 0x02;
vic_waitTop();
vic_waitBottom();
// Switch to offset 0, 4
vic.ctrl2 = 0x00;
// Wait until bottom of section
vic_waitLine(50 + 8 * VSPLIT);
// Scroll upper section
for(char x=0; x<39; x++)
{
#assign ty 0
#repeat
sline(0, ty)[x] = sline(1, ty)[x];
cline(0, ty)[x] = cline(1, ty)[x];
#assign ty ty + 1
#until ty == VSPLIT
}
// Update column
#assign ty 0
#repeat
sline(0, ty)[39] = news[ty];
cline(0, ty)[39] = newc[ty];
#assign ty ty + 1
#until ty == VSPLIT
// Wait for bottom of visible screen
vic_waitBottom();
// Switch to offset 6, 4
vic.ctrl2 = 0x06;
// Scroll lower part of the screen, while top is redrawn
for(char x=0; x<39; x++)
{
#assign ty VSPLIT
#repeat
sline(0, ty)[x] = sline(1, ty)[x];
cline(0, ty)[x] = cline(1, ty)[x];
#assign ty ty + 1
#until ty == 25
}
// Update new column
#assign ty VSPLIT
#repeat
sline(0, ty)[39] = news[ty];
cline(0, ty)[39] = newc[ty];
#assign ty ty + 1
#until ty == 25
// Wait for bottom
vic_waitBottom();
// Now back to 4, 4
vic.ctrl2 = 0x04;
}
// Scroll one character right in two pixel increments
void scroll_right(void)
{
vic_waitTop();
vic_waitBottom();
vic.ctrl2 = 0x06;
vic_waitLine(50 + 8 * VSPLIT);
for(char x=39; x>0; x--)
{
#assign ty 0
#repeat
sline(0, ty)[x] = sline(-1, ty)[x];
cline(0, ty)[x] = cline(-1, ty)[x];
#assign ty ty + 1
#until ty == VSPLIT
}
#assign ty 0
#repeat
sline(0, ty)[0] = news[ty];
cline(0, ty)[0] = newc[ty];
#assign ty ty + 1
#until ty == VSPLIT
vic_waitBottom();
vic.ctrl2 = 0x00;
for(char x=39; x>0; x--)
{
#assign ty VSPLIT
#repeat
sline(0, ty)[x] = sline(-1, ty)[x];
cline(0, ty)[x] = cline(-1, ty)[x];
#assign ty ty + 1
#until ty == 25
}
#assign ty VSPLIT
#repeat
sline(0, ty)[0] = news[ty];
cline(0, ty)[0] = newc[ty];
#assign ty ty + 1
#until ty == 25
vic_waitBottom();
vic.ctrl2 = 0x02;
vic_waitTop();
vic_waitBottom();
vic.ctrl2 = 0x04;
}
// Scroll one character up in two pixel increments
void scroll_up(void)
{
vic_waitTop();
vic_waitBottom();
vic.ctrl1 = 0x02 | VIC_CTRL1_DEN;
vic_waitTop();
vic_waitBottom();
vic.ctrl1 = 0x00 | VIC_CTRL1_DEN;
vic_waitLine(50 + 8 * VSPLIT);
for(char x=0; x<40; x++)
{
#assign ty 0
#repeat
sline(0, ty)[x] = sline(0, ty + 1)[x];
cline(0, ty)[x] = cline(0, ty + 1)[x];
#assign ty ty + 1
#until ty == VSPLIT
}
vic_waitBottom();
vic.ctrl1 = 0x06 | VIC_CTRL1_DEN;
for(char x=0; x<40; x++)
{
#assign ty VSPLIT
#repeat
sline(0, ty)[x] = sline(0, ty + 1)[x];
cline(0, ty)[x] = cline(0, ty + 1)[x];
#assign ty ty + 1
#until ty == 24
sline(0, ty)[x] = news[x];
cline(0, ty)[x] = newc[x];
}
vic_waitBottom();
vic.ctrl1 = 0x04 | VIC_CTRL1_DEN;
}
char tmp0[40], tmp1[40], tmp2[40], tmp3[40];
// Scroll one character down in two pixel increments. This is more tricky than
// the other three cases, because we have to work towards the beam because
// we have to copy backwards in memory.
//
// The scroll is split into three sections, the seam rows are saved into
// intermediate arrays, so we can copy the top section first and the bottom
// section last, and stay ahead of the beam.
void scroll_down(void)
{
// Wait one frame
vic_waitTop();
vic_waitBottom();
// Save seam lines
for(char x=0; x<40; x++)
{
tmp0[x] = sline(0, VSPLIT)[x];
tmp1[x] = cline(0, VSPLIT)[x];
tmp2[x] = sline(0, VSPLIT2)[x];
tmp3[x] = cline(0, VSPLIT2)[x];
}
// Now switch to 4, 6
vic.ctrl1 = 0x06 | VIC_CTRL1_DEN;
// Wait for bottom of top section
vic_waitLine(58 + 8 * VSPLIT);
// Scroll top section down and copy new column
for(char x=0; x<40; x++)
{
#assign ty VSPLIT
#repeat
sline(0, ty)[x] = sline(0, ty - 1)[x];
cline(0, ty)[x] = cline(0, ty - 1)[x];
#assign ty ty - 1
#until ty == 0
sline(0, ty)[x] = news[x];
cline(0, ty)[x] = newc[x];
}
// vic_waitBottom();
// We have already reached the bottom, switch to 4, 0
vic.ctrl1 = 0x00 | VIC_CTRL1_DEN;
// Copy the second section, update the seam line from the buffer
for(char x=0; x<40; x++)
{
#assign ty VSPLIT2
#repeat
sline(0, ty)[x] = sline(0, ty - 1)[x];
cline(0, ty)[x] = cline(0, ty - 1)[x];
#assign ty ty - 1
#until ty == VSPLIT + 1
sline(0, ty)[x] = tmp0[x];
cline(0, ty)[x] = tmp1[x];
}
// Copy the third section, update the seam line from the buffer
for(char x=0; x<40; x++)
{
#assign ty 24
#repeat
sline(0, ty)[x] = sline(0, ty - 1)[x];
cline(0, ty)[x] = cline(0, ty - 1)[x];
#assign ty ty - 1
#until ty == VSPLIT2 + 1
sline(0, ty)[x] = tmp2[x];
cline(0, ty)[x] = tmp3[x];
}
// Switch to 4, 2
vic_waitBottom();
vic.ctrl1 = 0x02 | VIC_CTRL1_DEN;
// Switch to 4, 4
vic_waitTop();
vic_waitBottom();
vic.ctrl1 = 0x04 | VIC_CTRL1_DEN;
}
char grid[16][16];
#pragma align(grid, 256)
int main(void)
{
// We need some more accurate timing for this, so kill the kernal IRQ
__asm
{
sei
}
// Init the grid
for(char y=0; y<16; y++)
{
for(char x=0; x<16; x++)
{
grid[y][x] = rand() & 3;
}
}
char gridX = 0, gridY = 0;
// Inital drwaing of the screen
char * dp = Screen, * cp = Color;
for(char y=0; y<25; y++)
{
expandrow(dp, cp, &(grid[y >> 2][0]), 4 * (y & 3), 0);
dp += 40;
cp += 40;
}
// setup initial scroll offset
vic.ctrl1 = 0x04 | VIC_CTRL1_DEN;
vic.ctrl2 = 0x04;
for(;;)
{
// Check the joystick
joy_poll(0);
if (joyx[0] == 1)
{
// Move to the right
if (gridX < 24)
{
gridX++;
expandcol(news, newc, &(grid[gridY >> 2][(gridX + 39) >> 2]), 4 * (gridY & 3), (gridX + 39) & 3);
scroll_left();
}
}
else if (joyx[0] == -1)
{
// Move to the left
if (gridX > 0)
{
gridX--;
expandcol(news, newc, &(grid[gridY >> 2][gridX >> 2]), 4 * (gridY & 3), gridX & 3);
scroll_right();
}
}
else if (joyy[0] == 1)
{
// Move down
if (gridY < 39)
{
gridY++;
expandrow(news, newc, &(grid[(gridY + 24) >> 2][gridX >> 2]), 4 * ((gridY + 24) & 3), gridX & 3);
scroll_up();
}
}
else if (joyy[0] == -1)
{
// Move up
if (gridY > 0)
{
gridY--;
expandrow(news, newc, &(grid[gridY >> 2][gridX >> 2]), 4 * (gridY & 3), gridX & 3);
scroll_down();
}
}
}
return 0;
}