diff --git a/presets/c64/bcd.c b/presets/c64/bcd.c index a8ba6d6a..6f56db5e 100644 --- a/presets/c64/bcd.c +++ b/presets/c64/bcd.c @@ -1,5 +1,5 @@ -#include "neslib.h" +#include "common.h" word bcd_add(word a, word b) { register word c, d; // intermediate values diff --git a/presets/c64/climber.c b/presets/c64/climber.c new file mode 100644 index 00000000..12051a32 --- /dev/null +++ b/presets/c64/climber.c @@ -0,0 +1,689 @@ + +#include +#include + +#include +#include + +#include "bcd.h" +//#link "bcd.c" + +#include "common.h" +//#link "common.c" + +#include "scrolling.h" +//#link "scrolling.c" + +#include "sprites.h" +//#link "sprites.c" + +// indices of sound effects (0..3) +typedef enum { SND_START, SND_HIT, SND_COIN, SND_JUMP } SFXIndex; + +///// DEFINES + +#define MAX_FLOORS 20 // total # of floors in a stage +#define GAPSIZE 4 // gap size in tiles +#define BOTTOM_FLOOR_Y 2 // offset for bottommost floor + +#define MAX_ACTORS 5 // max # of moving actors +#define SCREEN_Y_BOTTOM 208 // bottom of screen in pixels +#define ACTOR_MIN_X 16 // leftmost position of actor +#define ACTOR_MAX_X 228 // rightmost position of actor +#define ACTOR_SCROLL_UP_Y 110 // min Y position to scroll up +#define ACTOR_SCROLL_DOWN_Y 140 // max Y position to scroll down +#define JUMP_VELOCITY 18 // Y velocity when jumping + +#define LADDER_XOFS -21 +#define XOFS 34 +#define BOTTOM_Y 242 +#define START_ORIGIN_Y (0xff - ROWS) +#define GAP_OFS_X -16 + +// constants for various tiles +#define CH_BLANK 0x20 +#define CH_WALL 0x7f +#define CH_FLOOR 0x66 +#define CH_LADDER1 0x6b +#define CH_LADDER2 0x73 +#define CH_ITEM 0x04 + +#define COLOR_LEVEL 0x01 +#define COLOR_LADDER 0x03 + +///// CHARS + +const byte CHAR_TABLE[8][8] = { + /*{w:8,h:8,brev:1,count:8}*/ + {0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00}, + {0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF}, + {0xFF,0xBF,0xBF,0x00,0xFF,0xFB,0xFB,0x00}, + {0x81,0xFF,0x81,0x81,0x81,0xFF,0x81,0x81}, + {0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00}, + {0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00}, + {0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00}, + {0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00}, +}; + +const byte static_sprite_table[2][16*2] = { + /*{w:16,h:16,brev:1,remap:[4,0,1,2,3,5,6,7,8,9],count:4}*/ + { + 0x00, 0x00, 0x00, 0x00, 0x00, 0x03, 0x02, 0x3F, + 0x35, 0x2A, 0x20, 0x20, 0x20, 0x20, 0x20, 0x3F, + 0x00, 0x00, 0x00, 0x00, 0x00, 0xC0, 0x40, 0xFC, + 0x54, 0xAC, 0x04, 0x04, 0x04, 0x04, 0x04, 0xFC, + },{ + 0x00, 0x19, 0x1F, 0x0A, 0x05, 0x07, 0x0E, 0x1C, + 0x3A, 0x2A, 0x3C, 0x2E, 0x1E, 0x18, 0x0E, 0x07, + 0x00, 0x80, 0x00, 0x00, 0x00, 0xC0, 0xF0, 0x38, + 0xFC, 0xF4, 0x7C, 0xB4, 0xB8, 0x68, 0xF0, 0xE0, + } +}; + +const byte ITEM_CHARS[3][4] = { + { 0xe9,0xe9,0x5f,0x69 }, + { 0x7f,0x7f,0x7f,0x7f }, + { 0x7f,0x7f,0x7f,0x7f }, +}; + +/*{w:12,h:21,bpp:2,brev:1,count:5,aspect:2}*/ +const char SPRITE_DATA[5][3*21] = { + { + 0x00,0x00,0x00,0x00,0xA8,0x00,0x02,0xEA,0x00, + 0x02,0xEA,0x00,0x02,0xEA,0x00,0x02,0xEA,0x00, + 0x00,0xA8,0x50,0x00,0x15,0x50,0x00,0xAA,0x50, + 0x00,0xAA,0x50,0x00,0xAA,0x50,0x0A,0xAA,0x50, + 0x02,0xAA,0x50,0x00,0xFF,0x00,0x00,0xAA,0x00, + 0x00,0xAA,0x00,0x00,0xA2,0x80,0x00,0xA2,0x80, + 0x00,0x22,0x80,0x01,0x51,0x40,0x01,0x51,0x40 + }, + { + 0x00,0x00,0x00,0x00,0xA8,0x00,0x02,0xAA,0x00, + 0x02,0xAA,0x00,0x22,0xAA,0x00,0x22,0xAA,0x00, + 0x20,0xA8,0x00,0x28,0x54,0x00,0x2A,0x56,0x80, + 0x0A,0x56,0xA0,0x02,0x56,0xA0,0x02,0x56,0x20, + 0x02,0x56,0x20,0x03,0xFF,0x20,0x02,0xAA,0x00, + 0x02,0x8A,0x00,0x02,0x8A,0x00,0x01,0x4A,0x00, + 0x05,0x4A,0x00,0x00,0x05,0x00,0x00,0x05,0x40 + }, + { + 0x00,0x00,0x00,0x00,0xA8,0x00,0x02,0xEA,0x00, + 0x02,0xEA,0x00,0x02,0xEA,0x00,0x02,0xEA,0x00, + 0x00,0xA8,0x50,0x00,0x15,0x50,0x00,0xAA,0x50, + 0x20,0xAA,0x50,0x2A,0xAA,0x50,0x0A,0xAA,0x50, + 0x00,0xAA,0x50,0x00,0xFF,0x00,0x00,0xAA,0x00, + 0x00,0xAA,0x00,0x0A,0xA2,0x80,0x0A,0xA2,0x94, + 0x0A,0x02,0x94,0x15,0x00,0x94,0x15,0x00,0x04 + }, + { + 0x00,0x00,0x00,0x00,0xA8,0x00,0x02,0xFE,0x00, + 0x02,0xBA,0x00,0x02,0xBA,0x00,0x02,0xBA,0x00, + 0x00,0xA8,0x00,0x00,0x54,0x08,0x82,0xAA,0xA8, + 0xAA,0xFE,0xA0,0x2A,0xAA,0x80,0x0A,0xFE,0x00, + 0x02,0xAA,0x00,0x03,0xFF,0x00,0x02,0xAA,0x00, + 0x02,0xAA,0x00,0x0A,0x8A,0x80,0x0A,0x02,0x80, + 0x0A,0x02,0x80,0x05,0x01,0x40,0x15,0x01,0x50 + }, + { + 0x00,0x00,0x00,0x00,0xA8,0x00,0x02,0xEA,0x00, + 0x02,0xEA,0x00,0x02,0xEA,0x00,0x02,0xEA,0x00, + 0x00,0xA8,0x50,0x00,0x15,0x50,0x00,0xAA,0x50, + 0x08,0xAA,0x90,0x0A,0xAA,0xA0,0x02,0xAA,0x60, + 0x00,0xAA,0x50,0x00,0xFF,0x00,0x00,0xAA,0x00, + 0x00,0xA2,0x00,0x00,0xA8,0x80,0x00,0x2A,0x80, + 0x02,0x8A,0x90,0x01,0x40,0x50,0x05,0x41,0x40 + }, +}; + +#define NUM_SPRITE_PATTERNS 5 +#define NUM_SPRITE_STATES 4 + +///// GLOBALS + +// random byte between (a ... b-1) +// use rand() because rand8() has a cycle of 255 +byte rndint(byte a, byte b) { + return (rand() % (b-a)) + a; +} + +///// GAME LOGIC + +// struct definition for a single floor +typedef struct Floor { + byte ypos; // # of tiles from ground + int height:4; // # of tiles to next floor + int gap:4; // X position of gap + int ladder1:4; // X position of first ladder + int ladder2:4; // X position of second ladder + int objtype:4; // item type (FloorItem) + int objpos:4; // X position of object +} Floor; + +// various items the player can pick up +typedef enum FloorItem { ITEM_NONE, ITEM_MINE, ITEM_HEART, ITEM_POWER }; + +// array of floors +Floor floors[MAX_FLOORS]; + +// is this x (pixel) position within the gap ? +bool is_in_gap(byte x, byte gap) { + if (gap) { + byte x1 = gap*16 + GAP_OFS_X; + return (x > x1+2 && x < x1+GAPSIZE*8-14); + } else { + return false; + } +} + +// is this ladder at (tile) position x within the gap? +bool ladder_in_gap(byte x, byte gap) { + return gap && x >= gap && x < gap+GAPSIZE*2; +} + +// create floors at start of game +void make_floors() { + byte i; + byte y = BOTTOM_FLOOR_Y; + Floor* prevlev = &floors[0]; + // create data for each floor + for (i=0; iheight = rndint(4,8); + do { + // only have gaps in higher floors + lev->gap = i>=3 ? rndint(0,COLS/2-5) : 0; + } while (ladder_in_gap(prevlev->ladder1, lev->gap) || + ladder_in_gap(prevlev->ladder2, lev->gap)); + do { + lev->ladder1 = rndint(2,COLS-6); + lev->ladder2 = rndint(2,COLS-6); + } while (ladder_in_gap(lev->ladder1, lev->gap) || + ladder_in_gap(lev->ladder2, lev->gap)); + if (i > 0) { + lev->objtype = rndint(1,3); + do { + lev->objpos = rndint(2,COLS-7); + } while (ladder_in_gap(lev->objpos, lev->gap)); + } + lev->ypos = y; + y += lev->height; + prevlev = lev; + } + // top floor is special + floors[MAX_FLOORS-1].height = 15; + floors[MAX_FLOORS-1].gap = 0; + floors[MAX_FLOORS-1].ladder1 = 0; + floors[MAX_FLOORS-1].ladder2 = 0; + floors[MAX_FLOORS-1].objtype = 0; +} + +// creete actors on floor_index, if slot is empty +void create_actors_on_floor(byte floor_index); + +// draw a nametable line into the frame buffer at +// 0 == bottom of stage +void scroll_draw_row(byte row) { + char *buf; // nametable buffer + char *attrs; // attribute buffer + byte floor; // floor counter + byte dy; // height in rows above floor + byte row_height; // rows above bottom of level + // set screen/color buffer addresses + buf = hidbuf + row*COLS; + attrs = tempbuf + row*COLS; + row_height = -(row + origin_y); + // loop over all floors + for (floor=0; floorypos; + // if below bottom floor (in basement) + if (dy >= 255 - BOTTOM_FLOOR_Y) dy = 0; + // does this floor intersect the desired row? + if (dy < lev->height) { + // set all colors to default + memset(attrs, COLOR_LEVEL, COLS); + // first two rows (floor)? + if (dy == 0) { + // draw floor + memset(buf, CH_FLOOR, COLS); + // is there a gap? if so, clear bytes + if (lev->gap) + memset(buf+lev->gap*2, CH_BLANK, GAPSIZE); + } else { + // clear buffer + memset(buf, CH_BLANK, COLS); + // draw walls + if (floor < MAX_FLOORS-1) { + buf[1] = CH_WALL; // left side + buf[COLS-2] = CH_WALL; // right side + } + // draw ladders + if (lev->ladder1) { + byte i = lev->ladder1*2; + buf[i] = CH_LADDER1; // left + buf[i+1] = CH_LADDER2; // right + attrs[i] = COLOR_LADDER; + attrs[i+1] = COLOR_LADDER; + } + if (lev->ladder2) { + byte i = lev->ladder2*2; + buf[i] = CH_LADDER1; // left + buf[i+1] = CH_LADDER2; // right + attrs[i] = COLOR_LADDER; + attrs[i+1] = COLOR_LADDER; + } + } + // draw object, if it exists + if (lev->objtype && (dy == 1 || dy == 2)) { + const byte* ichars = ITEM_CHARS[lev->objtype - 1]; + byte i = lev->objpos*2; + if (dy == 1) { + buf[i+0] = ichars[2]; // bottom-left + buf[i+1] = ichars[3]; // bottom-right + } else { + buf[i+0] = ichars[0]; // top-left + buf[i+1] = ichars[1]; // top-right + } + attrs[i+0] = 0x7; + attrs[i+1] = 0x7; + } + break; + } + } + // create actors on this floor, if needed + // TODO: maybe this happens too early? + if (dy == 0 && (floor >= 2)) { + create_actors_on_floor(floor); + } +} + +// not used, since we don't scroll left/right +void scroll_draw_column(byte col) { + col=col; +} + +// draw entire stage at current scroll position +// filling up entire name table +void draw_entire_stage() { + byte y; + for (y=0; yonscreen) { + Floor *level = &floors[level_index]; + a->state = WALKING; + a->color1 = level->ladder1 ^ level->ladder2; + a->color2 = level->ladder2; + a->name = 0; + a->x = level->ladder1 ^ (level->ladder2<<3) ^ (level->gap<<6); + a->yy = get_floor_yy(level_index); + a->level = level_index; + } +} + +static word yscroll; + +void draw_actor(byte i) { + byte name; + struct Actor* a = &actors[i]; + word screen_y = yscroll - a->yy; + if (screen_y > 240) { + a->onscreen = 0; + return; // offscreen vertically + } + name = 32 + (a->state - WALKING); + switch (a->state) { + case INACTIVE: + a->onscreen = 0; + return; // inactive, offscreen + case WALKING: + name += (a->x & 4); + case JUMPING: + case FALLING: + //name = a->dir ? 16*4 : 0; + break; + case CLIMBING: + //name += (a->yy & 4) ? 16*4 : 0; + break; + } + sprite_draw(i, a->x + XOFS, screen_y, name); + sprshad.spr_color[i] = a->color1; + a->onscreen = 1; +} + +void refresh_actors() { + byte i; + yscroll = BOTTOM_Y + scroll_fine_y + (START_ORIGIN_Y - origin_y)*8; + sprite_clear(); + for (i=0; i= MAX_FLOORS) return 0; + x = is_ladder_close(player_x, level->ladder1); + if (x) return x; + x = is_ladder_close(player_x, level->ladder2); + if (x) return x; + return 0; +} + +byte mount_ladder(Actor* player, signed char level_adjust) { + byte x = get_closest_ladder(player->x, player->level + level_adjust); + if (x) { + player->x = x + 8; + player->state = CLIMBING; + player->level += level_adjust; + return 1; + } else + return 0; +} + +void check_scroll_up() { + if (sprshad.spr_pos[0].y < BOTTOM_Y/2+16) { + scroll_vert(1); + } +} + +void check_scroll_down() { + if (sprshad.spr_pos[0].y > BOTTOM_Y/2+32 && origin_y < START_ORIGIN_Y) { + scroll_vert(-1); + } +} + +void fall_down(struct Actor* actor) { + actor->level--; + actor->state = FALLING; + actor->xvel = 0; + actor->yvel = 0; +} + +void move_actor(struct Actor* actor, byte joystick, bool scroll) { + switch (actor->state) { + + case WALKING: + // left/right has priority over climbing + if (joystick & JOY_BTN_1_MASK) { + actor->state = JUMPING; + actor->xvel = 0; + actor->yvel = 15; + if (joystick & JOY_LEFT_MASK) actor->xvel = -1; + if (joystick & JOY_RIGHT_MASK) actor->xvel = 1; + } else if (joystick & JOY_LEFT_MASK) { + actor->x--; + actor->dir = 1; + } else if (joystick & JOY_RIGHT_MASK) { + actor->x++; + actor->dir = 0; + } else if (joystick & JOY_UP_MASK) { + mount_ladder(actor, 0); // state -> CLIMBING + } else if (joystick & JOY_DOWN_MASK) { + mount_ladder(actor, -1); // state -> CLIMBING, level -= 1 + } else { + //actor->state = STANDING; + } + if (scroll) { + check_scroll_up(); + check_scroll_down(); + } + break; + + case CLIMBING: + if (joystick & JOY_UP_MASK) { + if (actor->yy >= get_ceiling_yy(actor->level)) { + actor->level++; + actor->state = WALKING; + } else { + actor->yy++; + } + if (scroll) check_scroll_up(); + } else if (joystick & JOY_DOWN_MASK) { + if (actor->yy <= get_floor_yy(actor->level)) { + actor->state = WALKING; + } else { + actor->yy--; + } + } + if (scroll) { + check_scroll_up(); + check_scroll_down(); + } + break; + + case FALLING: + if (scroll) { + check_scroll_up(); + check_scroll_down(); + } + case JUMPING: + actor->x += actor->xvel; + actor->yy += actor->yvel/4; + actor->yvel -= 1; + if (actor->yy <= get_floor_yy(actor->level)) { + actor->yy = get_floor_yy(actor->level); + actor->state = WALKING; + if (scroll) check_scroll_down(); + } + break; + } + // don't allow player to travel past left/right edges of screen + if (actor->x == 0) actor->x = 255; // we wrapped around right edge + if (actor->x == 1) actor->x = 2; // left edge + // if player lands in a gap, they fall (switch to JUMPING state) + if (actor->state == WALKING && + is_in_gap(actor->x, floors[actor->level].gap)) { + fall_down(actor); + } +} + +void pickup_object(Actor* actor) { + Floor* level = &floors[actor->level]; + byte objtype = level->objtype; + if (objtype && actor->state == WALKING) { + byte objx = level->objpos * 16 + 16 - XOFS; + if (actor->x >= objx && actor->x < objx+16) { + level->objtype = 0; + refresh_screen(); + } + } +} + +void move_player() { + char joy = joy_read(0); + move_actor(&actors[0], joy, true); + pickup_object(&actors[0]); +} + +byte iabs(int x) { + return x >= 0 ? x : -x; +} + +bool check_collision(Actor* a) { + byte i; + for (i=1; ilevel == b->level && + iabs(a->yy - b->yy) < 8 && + iabs(a->x - b->x) < 8) { + return true; + } + } + return false; +} + +/// + +void preview_stage() { + /* + scroll_y = floors[MAX_LEVELS-1].ypos; + while (scroll_y > 0) { + wait_vblank(); + refresh_screen(); + refresh_actors(); + scroll_y--; + } + */ +} + +void draw_blimp(struct cvu_sprite* sprite) { + /* + sprite->name = 48; + wait_vblank(); + cvu_set_sprite(SPRITES, 28, sprite); + sprite->name += 4; + sprite->x += 16; + cvu_set_sprite(SPRITES, 29, sprite); + sprite->name += 4; + sprite->x += 16; + cvu_set_sprite(SPRITES, 30, sprite); + sprite->name += 4; + sprite->x += 16; + cvu_set_sprite(SPRITES, 31, sprite); + refresh_actors(); + */ + sprite=sprite; +} + +void blimp_pickup_scene() { + /* + struct cvu_sprite sprite; + byte player_screen_y = cvu_vinb(SPRITES + 0); // sprite Y pos + sprite.x = actors[0].x-14; + sprite.y = 240; + sprite.tag = 0x8f; + while (sprite.y != player_screen_y-16) { + draw_blimp(&sprite); + sprite.x -= 48; + sprite.y++; + } + while (sprite.y != 240) { + draw_blimp(&sprite); + sprite.x -= 48; + sprite.y--; + actors[0].yy++; + } + */ +} + +void play_scene() { + byte i; + + memset(actors, 0, sizeof(actors)); + actors[0].state = WALKING; + actors[0].color1 = 0x1; + actors[0].color2 = 0xb; + actors[0].name = 0; + actors[0].x = 64; + actors[0].yy = get_floor_yy(0); + actors[0].level = 0; + + create_actors_on_floor(2); + refresh_screen(); + + while (actors[0].level != MAX_FLOORS-1) { + refresh_actors(); + move_player(); + // move all the actors + for (i=1; i 0 && check_collision(&actors[0])) { + fall_down(&actors[0]); + } + } + wait_vblank(); + scroll_update(); + sprite_update(visbuf); + } + + blimp_pickup_scene(); +} + +// MAIN + +// main program +void main() { + byte i; + + // set up scrolling + scroll_setup(); + // set up sprites + sprite_clear(); + for (i=0; i<5; i++) { + sprite_shape(hidbuf, 32+i, SPRITE_DATA[i]); + } + sprshad.spr_mcolor = 0xff; + sprshad.spr_mcolor0 = 0x0f; + sprshad.spr_mcolor1 = 0x00; + // set up character set + VIC.addr = 0x15; + // start scrolling @ bottom of level + origin_y = START_ORIGIN_Y; + joy_install (joy_static_stddrv); + make_floors(); + draw_entire_stage(); + play_scene(); + /* + while (1) { + scroll_vert(2); + scroll_update_regs(); + wait_vblank(); + } + */ +} diff --git a/presets/c64/common.c b/presets/c64/common.c new file mode 100644 index 00000000..04df0751 --- /dev/null +++ b/presets/c64/common.c @@ -0,0 +1,10 @@ + +#include "common.h" + +void raster_wait(unsigned char line) { + while (VIC.rasterline < line) ; +} + +void wait_vblank(void) { + raster_wait(255); +} diff --git a/presets/c64/common.h b/presets/c64/common.h new file mode 100644 index 00000000..583c7e92 --- /dev/null +++ b/presets/c64/common.h @@ -0,0 +1,18 @@ +#ifndef _COMMON_H +#define _COMMON_H + +#include +#include + +typedef uint8_t byte; +typedef uint16_t word; +typedef int8_t sbyte; +typedef enum { false, true } bool; + +#define COLS 40 +#define ROWS 25 + +void raster_wait(unsigned char line); +void wait_vblank(void); + +#endif diff --git a/presets/c64/joymove.c b/presets/c64/joymove.c index ea584a52..9dc951de 100644 --- a/presets/c64/joymove.c +++ b/presets/c64/joymove.c @@ -19,6 +19,17 @@ const char sprite[3*21] = { 0x00,0x3E,0x00,0x00,0x3E,0x00,0x00,0x1C,0x00 }; +/*{w:12,h:21,bpp:2,brev:1}*/ +const char spritemc[3*21] = { + 0x00,0xFF,0xC0,0x03,0xFF,0xF0,0x0F,0xFF,0xFC, + 0x0F,0xFB,0xFC,0x0F,0xEE,0xFC,0x0F,0xEF,0xFC, + 0x0F,0xEE,0xFC,0x0F,0xFB,0xFC,0x0F,0xFF,0xFC, + 0x09,0xFF,0xD8,0x08,0x7F,0x48,0x08,0x1D,0x08, + 0x02,0x0C,0x20,0x02,0x0C,0x20,0x02,0x0C,0x20, + 0x00,0x8C,0x80,0x00,0x8C,0x80,0x00,0x55,0x40, + 0x00,0x77,0x40,0x00,0x5D,0x40,0x00,0x15,0x00 +}; + // Raster wait with line argument void rasterWait(unsigned char line) { while (VIC.rasterline < line) ; @@ -28,6 +39,7 @@ int main (void) { int n; int x,y; + char bgcoll; // install the joystick driver joy_install (joy_static_stddrv); // set background color @@ -36,15 +48,21 @@ int main (void) __asm__("SEI"); // set sprite bitmap data for (n = 0 ; n < sizeof(sprite) ; n++) { - POKE(832 + n, sprite[n]); + POKE(0x340 + n, sprite[n]); + POKE(0x380 + n, spritemc[n]); } - // enable 1st sprite - VIC.spr_ena = 0x01; + // enable 1st and 2nd sprite + VIC.spr_ena = 0x03; + VIC.spr_mcolor = 0x02; + // set colors + VIC.spr_mcolor0 = 4; + VIC.spr_mcolor1 = 7; // 2x zoom 1st sprite VIC.spr_exp_x = 0x01; VIC.spr_exp_y = 0x01; // set address of sprite data - POKE(2040, 13); + POKE(0x7f8, 13); + POKE(0x7f9, 14); // set initial x/y positions x = 160; y = 128; @@ -59,13 +77,17 @@ int main (void) if (JOY_DOWN(joy)) ++y; // set VIC registers based on position VIC.spr0_x = x; - VIC.spr0_y = y; + VIC.spr0_y = y-32; + VIC.spr1_x = x; + VIC.spr1_y = y+32; VIC.spr_hi_x = (x & 256) ? 1 : 0; // change color when we collide with background - VIC.spr0_color = (VIC.spr_bg_coll & 1) ? 10 : 0; + bgcoll = VIC.spr_bg_coll; + VIC.spr0_color = (bgcoll & 1) ? 10 : 0; + VIC.spr1_color = (bgcoll & 2) ? 10 : 0; // wait for end of frame rasterWait(255); - } + } // uninstall joystick driver (not really necessary) joy_uninstall(); return EXIT_SUCCESS; diff --git a/presets/c64/scroll4.c b/presets/c64/scroll4.c index 6dbd9e79..846b0361 100644 --- a/presets/c64/scroll4.c +++ b/presets/c64/scroll4.c @@ -23,11 +23,11 @@ void wait_vblank() { raster_wait(255); // TODO } -sbyte scroll_fine_x = 0; -sbyte scroll_fine_y = 0; -byte origin_x = 0x80; -byte origin_y = 0x80; -byte curbuf = 0; +sbyte scroll_fine_x; +sbyte scroll_fine_y; +byte origin_x; +byte origin_y; +byte curbuf; byte* scrnbuf[2]; // screen buffer(s) byte tempbuf[COLS*ROWS]; @@ -138,6 +138,11 @@ void scroll_vert(sbyte delta_y) { } void scroll_setup() { + scroll_fine_x = 0; + scroll_fine_y = 0; + origin_x = 0x80; + origin_y = 0x80; + curbuf = 0; // get screen buffer addresses scrnbuf[0] = (byte*) 0x8000; scrnbuf[1] = (byte*) 0x8400; diff --git a/presets/c64/scroll5.c b/presets/c64/scroll5.c new file mode 100644 index 00000000..56c86a2d --- /dev/null +++ b/presets/c64/scroll5.c @@ -0,0 +1,93 @@ + +#include +#include +#include +#include +#include +#include +#include +#include + +#include "common.h" +//#link "common.c" + +#include "scrolling.h" +//#link "scrolling.c" + +#include "sprites.h" +//#link "sprites.c" + +static void draw_cell(byte x, byte y) { + byte xx = x + origin_x; + byte yy = y + origin_y; + byte ch = xx ^ yy; + word ofs = x+y*COLS; + hidbuf[ofs] = ch; // character + tempbuf[ofs] = ch; // color +} + +void scroll_draw_column(byte col) { + byte y; + for (y=0; y + +#include "scrolling.h" + +sbyte scroll_fine_x; +sbyte scroll_fine_y; +byte origin_x; +byte origin_y; +byte* hidbuf; +byte* visbuf; +byte tempbuf[COLS*ROWS]; +byte swap_needed; + +// + +void scroll_swap(void) { + byte* tmp; + // set VIC bank address + VIC.addr = (VIC.addr & 0xf) | (((word)hidbuf >> 8) << 2); + // swap hidden and visible buffers + tmp = hidbuf; + hidbuf = visbuf; + visbuf = tmp; +} + +void scroll_copy(void) { + // copy temp buf to color ram + memcpy(COLOR_RAM, tempbuf, COLS*ROWS); + // copy visible buffer to hidden buffer + memcpy(hidbuf, visbuf, COLS*ROWS); +} + +void scroll_update(void) { + VIC.ctrl1 = (VIC.ctrl1 & 0xf8) | scroll_fine_y; + VIC.ctrl2 = (VIC.ctrl2 & 0xf8) | scroll_fine_x; + if (swap_needed) { + scroll_swap(); + scroll_copy(); + swap_needed = 0; + } +} + +// TODO: left and up can be faster, b/c we can copy color ram downward + +void scroll_left(void) { + memcpy(hidbuf, visbuf+1, COLS*ROWS-1); + ++origin_x; + memcpy(tempbuf, COLOR_RAM+1, COLS*ROWS-1); + scroll_draw_column(COLS-1); + swap_needed = true; +} + +void scroll_up(void) { + memcpy(hidbuf, visbuf+COLS, COLS*(ROWS-1)); + ++origin_y; + memcpy(tempbuf, COLOR_RAM+COLS, COLS*(ROWS-1)); + scroll_draw_row(ROWS-1); + swap_needed = true; +} + +void scroll_right(void) { + memcpy(hidbuf+1, visbuf, COLS*ROWS-1); + --origin_x; + memcpy(tempbuf+1, COLOR_RAM, COLS*ROWS-1); + scroll_draw_column(0); + swap_needed = true; +} + +void scroll_down(void) { + memcpy(hidbuf+COLS, visbuf, COLS*(ROWS-1)); + --origin_y; + memcpy(tempbuf+COLS, COLOR_RAM, COLS*(ROWS-1)); + scroll_draw_row(0); + swap_needed = true; +} + +void scroll_horiz(sbyte delta_x) { + scroll_fine_x += delta_x; + while (scroll_fine_x < 0) { + scroll_fine_x += 8; + scroll_left(); + } + while (scroll_fine_x >= 8) { + scroll_fine_x -= 8; + scroll_right(); + } +} + +void scroll_vert(sbyte delta_y) { + scroll_fine_y += delta_y; + while (scroll_fine_y < 0) { + scroll_fine_y += 8; + scroll_up(); + } + while (scroll_fine_y >= 8) { + scroll_fine_y -= 8; + scroll_down(); + } +} + +void scroll_setup(void) { + scroll_fine_x = scroll_fine_y = 0; + origin_x = origin_y = 0x80; + swap_needed = true; + + // get screen buffer addresses + hidbuf = (byte*) 0x8000; + visbuf = (byte*) 0x8400; + // copy existing text to screen 0 + memcpy(hidbuf, (byte*)0x400, COLS*ROWS); + // copy screen 1 to screen 0 + memcpy(visbuf, hidbuf, COLS*ROWS); + + // set VIC bank ($4000-$7FFF) + // https://www.c64-wiki.com/wiki/VIC_bank + CIA2.pra = 0x01; + + VIC.ctrl1 &= ~0x08; // 24 lines + VIC.ctrl2 &= ~0x08; // 38 columns +} + diff --git a/presets/c64/scrolling.h b/presets/c64/scrolling.h new file mode 100644 index 00000000..ab4224d9 --- /dev/null +++ b/presets/c64/scrolling.h @@ -0,0 +1,33 @@ +#ifndef _SCROLLING_H +#define _SCROLLING_H + +#include "common.h" + +// drawable screen area (only shows 38 x 24) +#define COLS 40 +#define ROWS 25 + +extern sbyte scroll_fine_x; // X fine scroll (pixels) +extern sbyte scroll_fine_y; // Y fine scroll (pixels) +extern byte origin_x; // X scroll origin (columns) +extern byte origin_y; // Y scroll origin (rows) +extern byte* hidbuf; // hidden screen buffer(s) +extern byte* visbuf; // visible screen buffer(s) +extern byte tempbuf[COLS*ROWS]; // temporary buffer +extern byte swap_needed; // TRUE if scroll_update() swaps + +// call this at start of program +void scroll_setup(void); + +// scroll in X or Y directions +void scroll_horiz(sbyte delta_x); +void scroll_vert(sbyte delta_y); + +// call this after vblank +void scroll_update(void); + +// caller must implement these two +void scroll_draw_column(byte col); +void scroll_draw_row(byte row); + +#endif diff --git a/presets/c64/sprites.c b/presets/c64/sprites.c new file mode 100644 index 00000000..a95d8af4 --- /dev/null +++ b/presets/c64/sprites.c @@ -0,0 +1,40 @@ + +#include +#include "sprites.h" + +SpriteShadow sprshad; + +void sprite_update(char* screenram) { + memcpy(screenram + 0x3f8, sprshad.spr_shapes, 8); + VIC.spr_ena = sprshad.spr_ena; + VIC.spr_hi_x = sprshad.spr_hi_x; + memcpy(VIC.spr_pos, sprshad.spr_pos, 16); + memcpy(VIC.spr_color, sprshad.spr_color, 8); + VIC.spr_exp_x = sprshad.spr_exp_x; + VIC.spr_exp_y = sprshad.spr_exp_y; + VIC.spr_bg_prio = sprshad.spr_bg_prio; + VIC.spr_mcolor = sprshad.spr_mcolor; + VIC.spr_mcolor0 = sprshad.spr_mcolor0; + VIC.spr_mcolor1 = sprshad.spr_mcolor1; +} + +void sprite_shape(char* vicbank, byte index, const char* sprite_data) { + memcpy(vicbank + index*64, sprite_data, 64); +} + +void sprite_draw(byte i, word x, byte y, byte shape) { + byte mask = 1 << i; + sprshad.spr_ena |= mask; + if (x >> 8) + sprshad.spr_hi_x |= mask; + else + sprshad.spr_hi_x &= ~mask; + sprshad.spr_pos[i].x = x; + sprshad.spr_pos[i].y = y; + sprshad.spr_shapes[i] = shape; +} + +void sprite_clear(void) { + sprshad.spr_ena = 0; +} + diff --git a/presets/c64/sprites.h b/presets/c64/sprites.h new file mode 100644 index 00000000..c75c007e --- /dev/null +++ b/presets/c64/sprites.h @@ -0,0 +1,30 @@ +#ifndef _SPRITES_H +#define _SPRITES_H + +#include "common.h" + +typedef struct { + byte spr_ena; /* Enable sprites */ + byte spr_hi_x; /* High bits of X coordinate */ + byte spr_exp_x; /* Expand sprites in X dir */ + byte spr_exp_y; /* Expand sprites in Y dir */ + byte spr_bg_prio; /* Priority to background */ + byte spr_mcolor; /* Sprite multicolor bits */ + byte spr_mcolor0; /* Color 0 for multicolor sprites */ + byte spr_mcolor1; /* Color 1 for multicolor sprites */ + byte spr_color[8]; /* Colors for the sprites */ + struct { + byte x; /* X coordinate */ + byte y; /* Y coordinate */ + } spr_pos[8]; + byte spr_shapes[8]; /* sprite shapes */ +} SpriteShadow; + +extern SpriteShadow sprshad; + +void sprite_update(char* screenram); +void sprite_shape(char* vicbank, byte index, const char* sprite_data); +void sprite_draw(byte i, word x, byte y, byte shape); +void sprite_clear(void); + +#endif diff --git a/src/ide/pixeleditor.ts b/src/ide/pixeleditor.ts index 299f201e..2540f92b 100644 --- a/src/ide/pixeleditor.ts +++ b/src/ide/pixeleditor.ts @@ -309,6 +309,7 @@ var PREDEF_PALETTES = { 0x002844,0x002844, 0x184864,0x184864, 0x306884,0x306884, 0x4484a0,0x4484a0, 0x589cb8,0x589cb8, 0x6cb4d0,0x6cb4d0, 0x7ccce8,0x7ccce8, 0x8ce0fc,0x8ce0fc ], 'astrocade':[0,2368548,4737096,7171437,9539985,11974326,14342874,16777215,12255269,14680137,16716142,16725394,16734903,16744155,16753663,16762879,11534409,13959277,16318866,16721334,16730842,16740095,16749311,16758783,10420330,12779662,15138995,16718039,16727291,16736767,16745983,16755199,8847495,11206827,13631696,15994612,16724735,16733951,16743423,16752639,6946975,9306307,11731175,14092287,16461055,16732415,16741631,16751103,4784304,7143637,9568505,11929087,14297599,16731647,16741119,16750335,2425019,4784352,7209215,9570047,12004095,14372863,16741375,16750847,191,2359523,4718847,7146495,9515263,11949311,14318079,16752127,187,224,2294015,4658431,7092735,9461247,11895551,14264063,176,213,249,2367999,4736511,7105279,9539327,11908095,159,195,3303,209151,2577919,4946431,7380735,9749247,135,171,7888,17140,681983,3050495,5484543,7853311,106,3470,12723,22231,31483,1548031,3916799,6285311,73,8557,17810,27318,36570,373759,2742271,5176575,4389,13641,23150,32402,41911,51163,2026495,4456447,9472,18724,27976,37485,46737,56246,1834970,4194303,14080,23296,32803,42055,51564,60816,2031541,4456409,18176,27648,36864,46116,55624,392556,2752401,5177269,21760,30976,40192,49667,58919,1572683,3932016,6291348,24320,33536,43008,52224,716810,3079982,5504851,7864183,25856,35328,44544,250368,2619136,4980503,7405371,9764703,26624,35840,45312,2413824,4782336,7143173,9568041,11927374,26112,35584,2338560,4707328,7141376,9502464,11927326,14286659,24832,2393344,4762112,7196160,9564928,11992832,14352155,16711487,2447360,4815872,7250176,9618688,12052992,14417664,16776990,16777027,4803328,7172096,9606144,11974912,14343424,16776965,16777001,16777038,6962176,9330688,11764992,14133504,16502272,16773655,16777019,16777055,8858112,11226880,13660928,16029440,16759818,16769070,16777043,16777079,10426112,12794624,15163392,16745475,16754727,16764235,16773488,16777108,11534848,13969152,16337664,16740388,16749640,16759148,16768401,16777141,12255232,14684928,16725795,16735047,16744556,16753808,16763317,16772569], + 'c64':[0x000000,0xffffff,0x2b3768,0xb2a470,0x863d6f,0x438d58,0x792835,0x6fc7b8,0x254f6f,0x003943,0x59679a,0x444444,0x6c6c6c,0x84d29a,0xb55e6c,0x959595], }; var PREDEF_LAYOUTS : {[id:string]:PixelEditorPaletteLayout} = {