diff --git a/css/ui.css b/css/ui.css index 0e7b5fa6..f3a42d94 100644 --- a/css/ui.css +++ b/css/ui.css @@ -292,9 +292,9 @@ div.emuspacer { padding: 20px; background: #000; margin-top:40px; - margin-left:5%; - margin-right:5%; - width:90%; + margin-left:3%; + margin-right:3%; + width:94%; pointer-events:auto; } .emuvideo:focus { diff --git a/presets/c64/23matches.c b/presets/c64/23matches.c new file mode 100644 index 00000000..186856b7 --- /dev/null +++ b/presets/c64/23matches.c @@ -0,0 +1,80 @@ + +#include "common.h" +#include + +int matches; // number of matches remaining +int take; // # of matches to take on this turn + +void print_matches() { + printf("\nThere are %d matches left.\n", matches); +} + +void human_moves() { + print_matches(); + // loop until we get a valid move + while (1) { + printf("\n>> Your turn. Take 1, 2, or 3 matches?"); + // did we get exactly one input value? + if (scanf("%d", &take) == 1) { + // is it between 1 and 3? + if (take >= 1 && take <= 3) { + // are there not enough matches? + if (take > matches) { + printf("Not enough matches! Try again.\n"); + continue; // go back to start of loop + } else { + // take the appropriate number of matches + printf("You took %d matches.\n", take); + matches -= take; + break; // break out of loop + } + } + } + printf("Bad input! Type a number from 1 to 3.\n"); + } +} + +void computer_moves() { + print_matches(); + // simple AI: hard coded if 1-4 matches left + // otherwise take random number from 1 to 3 + switch (matches) { + case 1: take = 1; break; + case 2: take = 1; break; + case 3: take = 2; break; + case 4: take = 3; break; + default: take = (rand() % 3) + 1; break; + } + printf("\n<< My turn. I'll take %d matches.\n", take); + matches -= take; +} + +void play_game(void) { + printf( + "When it is your turn, you may take\n" + "1, 2 or 3 matches. I will do the same.\n\n" + "Whoever takes the last match loses.\n"); + + matches = 23; + // loop until no more matches + while (matches > 0) { + // move human, check if they lost + human_moves(); + if (matches == 0) { + printf("You lose, turkey!\nBetter luck next time.\n"); + break; + } + // move computer, check if they lost + computer_moves(); + if (matches == 0) { + printf("I lost! You must be good!\n"); + break; + } + } +} + +void main(void) { + clrscr(); + printf("*** 23 MATCHES ***\n\n"); + play_game(); +} diff --git a/presets/c64/badspacerobots-c64.multi.bin b/presets/c64/badspacerobots-c64.multi.bin new file mode 100644 index 00000000..203e9fc4 Binary files /dev/null and b/presets/c64/badspacerobots-c64.multi.bin differ diff --git a/presets/c64/basicheader.dasm b/presets/c64/basicheader.dasm index 7baf15c7..cb8e8fa4 100644 --- a/presets/c64/basicheader.dasm +++ b/presets/c64/basicheader.dasm @@ -5,6 +5,6 @@ .org $0801 ; start of BASIC program .word BASIC_END, 10 ; Next line and current line number - .byte $9e," 2064",0 ; SYS 2064 + .byte $9e," 2062",0 ; SYS 2062 BASIC_END: .word 0 ; End of program diff --git a/presets/c64/bcd.c b/presets/c64/bcd.c index 6f56db5e..9020d2c8 100644 --- a/presets/c64/bcd.c +++ b/presets/c64/bcd.c @@ -2,12 +2,19 @@ #include "common.h" word bcd_add(word a, word b) { - register word c, d; // intermediate values - c = a + 0x0666; // add 6 to each BCD digit - d = c ^ b; // sum without carry propagation - c += b; // provisional sum - d = ~(c ^ d) & 0x1110; // just the BCD carry bits - d = (d >> 2) | (d >> 3); // correction - return c - d; // corrected BCD sum + asm("sed"); + a += b; + asm("cld"); + return a; +} + +void draw_bcd_word(word address, word bcd) { + byte i; + address += 4; + for (i=0; i<4; i++) { + POKE(address, (bcd & 0b1111) + '0'); + address--; + bcd >>= 4; + } } diff --git a/presets/c64/bcd.h b/presets/c64/bcd.h index b8daf0d0..5dc1b336 100644 --- a/presets/c64/bcd.h +++ b/presets/c64/bcd.h @@ -1,3 +1,7 @@ +/* add two BCD numbers */ unsigned int bcd_add(unsigned int a, unsigned int b); -unsigned int bcd_add2(unsigned int a, unsigned int b); + +/* print a 16-bit BCD to a specific address */ +void draw_bcd_word(word address, word bcd_value); + diff --git a/presets/c64/c64-sid.cfg b/presets/c64/c64-sid.cfg new file mode 100644 index 00000000..a3c23039 --- /dev/null +++ b/presets/c64/c64-sid.cfg @@ -0,0 +1,49 @@ +FEATURES { + STARTADDRESS: default = $0801; +} +SYMBOLS { + __LOADADDR__: type = import; + __EXEHDR__: type = import; + __STACKSIZE__: type = weak, value = $0800; # 2k stack + __HIMEM__: type = weak, value = $8000; +} +MEMORY { + ZP: file = "", define = yes, start = $0002, size = $001A; + LOADADDR: file = %O, start = %S - 2, size = $0002; + HEADER: file = %O, define = yes, start = %S, size = $000D; + LOWMAIN: file = %O, define = yes, start = __HEADER_LAST__, size = $1000 - __HEADER_LAST__, fill = yes; + SIDFILE: file = %O, define = yes, start = $1000, size = $1000, fill = yes; + MAIN: file = %O, define = yes, start = $2000, size = __HIMEM__ - $2000; + BSS: file = "", start = __ONCE_RUN__, size = __HIMEM__ - __STACKSIZE__ - __ONCE_RUN__; + VICBANK: file = %O, start = $8000, size = $4000; +} +SEGMENTS { + ZEROPAGE: load = ZP, type = zp; + LOADADDR: load = LOADADDR, type = ro; + EXEHDR: load = HEADER, type = ro; + STARTUP: load = LOWMAIN, type = ro; + LOWCODE: load = LOWMAIN, type = ro, optional = yes; + SIDFILE: load = SIDFILE, type = ro, optional = yes; + CODE: load = MAIN, type = ro; + RODATA: load = MAIN, type = ro; + DATA: load = MAIN, type = rw; + INIT: load = MAIN, type = rw; + ONCE: load = MAIN, type = ro, define = yes; + BSS: load = BSS, type = bss, define = yes; + VICBANK: load = MAIN, type = ro, optional = yes; +} +FEATURES { + CONDES: type = constructor, + label = __CONSTRUCTOR_TABLE__, + count = __CONSTRUCTOR_COUNT__, + segment = ONCE; + CONDES: type = destructor, + label = __DESTRUCTOR_TABLE__, + count = __DESTRUCTOR_COUNT__, + segment = RODATA; + CONDES: type = interruptor, + label = __INTERRUPTOR_TABLE__, + count = __INTERRUPTOR_COUNT__, + segment = RODATA, + import = __CALLIRQ__; +} diff --git a/presets/c64/climber.c b/presets/c64/climber.c index 5f072a35..27f0d37b 100644 --- a/presets/c64/climber.c +++ b/presets/c64/climber.c @@ -1,24 +1,31 @@ -#include -#include - -#include -#include - -#include "bcd.h" -//#link "bcd.c" +//#resource "c64-sid.cfg" +#define CFGFILE c64-sid.cfg #include "common.h" //#link "common.c" +#include "sidplaysfx.h" +//#resource "sidmusic1.bin" +//#link "sidplaysfx.ca65" + +#include "rasterirq.h" +//#link "rasterirq.ca65" + +#include "bcd.h" +//#link "bcd.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; +// indices of sound effects +#define SND_JUMP 0 +#define SND_HIT 2 +#define SND_COIN 1 +#define SND_FALL 3 ///// DEFINES @@ -62,7 +69,7 @@ const byte ITEM_CHARS[3][4] = { #define NUM_SPRITE_PATTERNS 13 /*{w:12,h:21,bpp:2,brev:1,count:13,aspect:2}*/ -const char SPRITE_DATA[NUM_SPRITE_PATTERNS][3*21] = { +const char SPRITE_DATA[NUM_SPRITE_PATTERNS][64] = { // left direction { 0x00,0x00,0x00,0x00,0xA8,0x00,0x02,0xEA,0x00, @@ -372,8 +379,9 @@ void refresh_floor(byte floor) { byte explode_timer = 0; +#define SPRITE_SHAPE_FIRST 192 #define SPRITE_XPLODE 7 -#define SHAPE_XPLODE0 (32+10) +#define SHAPE_XPLODE0 (SPRITE_SHAPE_FIRST+10) #define NUM_XPLODE_SHAPES 3 void explode(int x, byte y) { @@ -446,7 +454,7 @@ void draw_actor(byte i) { a->onscreen = 0; return; // offscreen vertically } - name = 32 + (a->state - WALKING); + name = SPRITE_SHAPE_FIRST + (a->state - WALKING); switch (a->state) { case INACTIVE: a->onscreen = 0; @@ -469,7 +477,7 @@ void draw_actor(byte i) { void refresh_actors() { byte i; yscroll = BOTTOM_Y + scroll_fine_y + (START_ORIGIN_Y - origin_y)*8; - sprite_clear(); + sprshad.spr_ena = 0; // make all sprites invisible for (i=0; iyvel = 15; if (joystick & JOY_LEFT_MASK) actor->xvel = -1; if (joystick & JOY_RIGHT_MASK) actor->xvel = 1; + if (scroll) sid_sfx(SND_JUMP); } else if (joystick & JOY_LEFT_MASK) { actor->x--; actor->dir = 1; @@ -605,6 +614,7 @@ void move_actor(struct Actor* actor, byte joystick, bool scroll) { if (actor->state == WALKING && is_in_gap(actor->x, floors[actor->level].gap)) { fall_down(actor); + if (scroll) sid_sfx(SND_FALL); } } @@ -626,11 +636,11 @@ void pickup_object(Actor* actor) { if (objtype == ITEM_MINE) { // we hit a mine, fall down fall_down(actor); - //sfx_play(SND_HIT,0); + sid_sfx(SND_HIT); } else { // we picked up an object, add to score //score = bcd_add(score, 1); - //sfx_play(SND_COIN,0); + sid_sfx(SND_COIN); } } } @@ -717,6 +727,9 @@ void play_scene() { create_actors_on_floor(2); refresh_screen(); + sid_init(1); + sid_start(); + while (actors[0].level != MAX_FLOORS-1) { refresh_actors(); move_player(); @@ -728,6 +741,7 @@ void play_scene() { if (VIC.spr_coll & 0x01) { if (actors[0].level > 0 && check_collision(&actors[0])) { fall_down(&actors[0]); + sid_sfx(SND_HIT); } } if (swap_needed) sprite_update(hidbuf); @@ -739,26 +753,34 @@ void play_scene() { blimp_pickup_scene(); } +// main display list +void game_displaylist(void) { +// VIC.bordercolor = 2; + sid_update(); +// VIC.bordercolor = 0; +// DLIST_NEXT(42); +// VIC.bordercolor = 3; + DLIST_RESTART(20); +} + // main program void main() { - byte i; - // set up scrolling scroll_setup(); // set up sprites sprite_clear(); - for (i=0; i +#include +#include +#include #include +#include +#include +#include +#include -typedef uint8_t byte; -typedef uint16_t word; -typedef int8_t sbyte; -typedef enum { false, true } bool; +typedef uint8_t byte; // 8-bit unsigned +typedef int8_t sbyte; // 8-bit signed +typedef uint16_t word; // 16-bit unsigned +typedef enum { false, true } bool; // boolean -#define COLS 40 -#define ROWS 25 +#define COLS 40 // total # of columns +#define ROWS 25 // total # of rows -void raster_wait(unsigned char line); -void wait_vblank(void); + +///// MACROS ///// + +// lookup screen address macro +#define SCRNADR(base,col,row) ((base)+(col)+(row)*40) + +// default screen base address on startup +#define DEFAULT_SCREEN ((void*)0x400) + +// wait until next frame, same as waitvsync() +#define wait_vblank waitvsync +// is raster line > 255? +#define RASTER_HIBIT (VIC.ctrl1 & 0x80) + +// set VIC Bank (given the start address) +#define SET_VIC_BANK(_addr) \ + CIA2.pra = (CIA2.pra & ~3) | (((((_addr)>>8)&0xc0)>>6)^3); + +// set VIC character memory (given the start address) +#define SET_VIC_BITMAP(_addr) \ + VIC.addr = (VIC.addr & 0b11110001) | ((((_addr)>>8)&0x38)>>2); + +// set VIC screen memory (given the start address) +#define SET_VIC_SCREEN(_addr) \ + VIC.addr = (VIC.addr & 0b00001111) | ((((_addr)>>8)&0x3c)<<2); + +// set scrolling registers +#define SET_SCROLL_Y(_y) \ + VIC.ctrl1 = (VIC.ctrl1 & 0xf8) | (_y); + +#define SET_SCROLL_X(_x) \ + VIC.ctrl2 = (VIC.ctrl2 & 0xf8) | (_x); + + +// enable RAM from 0xa000-0xffff, disable interrupts +#define ENABLE_HIMEM() \ + asm("php"); \ + asm("sei"); \ + POKE(1, PEEK(1) & ~0b111); + +// enable ROM and interrupts +#define DISABLE_HIMEM() \ + POKE(1, PEEK(1) | 0b111); \ + asm("plp"); + +///// FUNCTIONS ///// + +// wait until specific raster line +void raster_wait(byte line); + +// get current VIC bank start address +char* get_vic_bank_start(); + +// get current screen memory address +char* get_screen_memory(); + +// return key in buffer, or 0 if none (BIOS call) +char __fastcall__ poll_keyboard(); #endif diff --git a/presets/c64/fullscrollgame.c b/presets/c64/fullscrollgame.c new file mode 100644 index 00000000..aad3860b --- /dev/null +++ b/presets/c64/fullscrollgame.c @@ -0,0 +1,638 @@ + +#include +#include +#include +#include +#include +#include +#include +#include + +//#resource "c64-sid.cfg" +#define CFGFILE c64-sid.cfg + +#include "common.h" +//#link "common.c" + +#include "scrolling.h" +//#link "scrolling2.c" + +#include "sprites.h" +//#link "sprites.c" + +//#link "level2.ca65" + +#define CAMERA_OFFSET_X 158 +#define CAMERA_OFFSET_Y 120 +#define CAMERA_MAX_DX 12 +#define CAMERA_MAX_DY 8 + +#define MAX_ACTORS 8 +#define ACTOR_OFFSET_X 28 +#define ACTOR_OFFSET_Y 30 +#define ACTOR_WIDTH 24 +#define ACTOR_HEIGHT 21 + +#define JUMP_VELOCITY -36 +#define MAX_FALL_VELOCITY 64 +#define MAX_HORIZ_VELOCITY 16 + +#define MAP_COLS 16 +#define MAP_ROWS 16 + +#define DEFAULT_CHAR chartileset_data[16] +#define DEFAULT_COLOR chartileset_colour_data[1] + +#define FLAG_SOLID 1 +#define FLAG_PLATFORM 2 +#define FLAG_LADDER 4 + + +// level map data +extern const byte charset_data[]; +extern const byte charset_attrib_data[]; +extern const byte chartileset_data[]; +extern const byte chartileset_colour_data[]; +extern const byte chartileset_tag_data[]; +extern const byte map_data[]; + + +static byte framecount; +static byte framemask; + +const byte BITMASKS[8] = { 0x01, 0x02, 0x04, 0x08, 0x10, 0x20, 0x40, 0x80 }; + +static byte tileflagmap[MAP_ROWS*MAP_COLS]; +static byte tileindex; +static byte tilechar; + +static bool get_cell_at(byte world_x, byte world_y) { + sbyte col = world_x >> 2; + sbyte row = world_y >> 2; + byte xofs = world_x & 3; + byte yofs = world_y & 3; + if (col < 0 || col >= MAP_COLS || row < 0 || row >= MAP_ROWS) { + return false; + } else { + tileindex = map_data[col + row * MAP_ROWS]; + tilechar = chartileset_data[xofs + (yofs + tileindex*4)*4]; + return true; + } +} + +byte compute_tile_flags() { + switch (tileindex) { + case 3: return FLAG_PLATFORM; + case 4: return FLAG_PLATFORM; + case 5: return FLAG_SOLID; + case 6: return FLAG_LADDER; + case 7: return FLAG_PLATFORM | FLAG_LADDER; + case 8: return FLAG_SOLID; + default: return 0; + } +} + +byte get_tile_flags(word world_x, word world_y) { + byte tilex = world_x >> 5; + byte tiley = world_y >> 5; + if (tilex < MAP_COLS && tiley < MAP_ROWS) + return tileflagmap[tilex + tiley*MAP_COLS]; + else + return 0; +} + +static void build_tile_flag_map(void) { + byte x,y; + byte i=0; + for (y=0; yxx + pixofs_x + fine_correct_x + ACTOR_OFFSET_X; + word ypos = actor->yy + pixofs_y + fine_correct_y + ACTOR_OFFSET_Y; + if (xpos > 320 || ypos > 250) { + ypos = 255; + } + switch (actor->state) { + case STANDING: + if (actor->xvel && actor->xx & 4) shape += 4; + if (!actor->faceleft) shape += 5; + break; + case JUMPING: + shape += 2; + if (!actor->faceleft) shape += 5; + break; + case CLIMBING: + shape += 1; + if (actor->yy & 2) shape += 5; + break; + } + sprite_draw(index, xpos, ypos, shape); +} + +const char velocity_bitmasks[8] = { + 0b00000000, // 0/8 + 0b00001000, // 1/8 + 0b00100010, // 2/8 + 0b10010010, // 3/8 + 0b01010101, // 4/8 + 0b01110101, // 5/8 + 0b11101110, // 6/8 + 0b11110111, // 7/8 +// 0b11111111, // 8/8 +}; + +static byte box[4]; // hit box + +/* +void actor_set_position(register Actor* actor, + word world_x, + word world_y, + ActorState state) { + actor->xx = world_x; + actor->yy = world_y; + actor->state = state; + actor->tileindex = (world_x>>5) | (world_y>>5)*MAP_COLS; +} +*/ + +void move_actor(register Actor* actor, + sbyte cmd_dx, + sbyte cmd_dy) { + word xx,yy; + byte flags; + sbyte dx = cmd_dx; + sbyte dy = cmd_dy; + bool state = actor->state; + + xx = actor->xx + dx; + yy = actor->yy + dy; + // if we are standing, move hit box 1 pixel below our feet + if (state == STANDING) { + yy += 1 - dy; // cancel out any climbing vertical offset (dy) + dy = 0; + } + // get hit box flags on all 4 corners + { + box[0] = get_tile_flags(xx, yy-ACTOR_HEIGHT); + box[1] = get_tile_flags(xx+ACTOR_WIDTH, yy-ACTOR_HEIGHT); + box[2] = get_tile_flags(xx, yy); + box[3] = get_tile_flags(xx+ACTOR_WIDTH, yy); + // cancel x velocity if either top corners is solid + if (dx < 0 && ((box[0] | box[2]) & FLAG_SOLID)) { + if (state != STANDING || box[0] & FLAG_SOLID) { + actor->xvel = 0; + dx = 0; + } + } + else if (dx > 0 && ((box[1] | box[3]) & FLAG_SOLID)) { + if (state != STANDING || box[1] & FLAG_SOLID) { + actor->xvel = 0; + dx = 0; + } + } + // cancel upward velocity if both top corners are solid + if (dy < 0 && ((box[0] | box[1]) & FLAG_SOLID)) { + actor->yvel = 0; + dy = 0; + } + switch (state) { + case JUMPING: + // are we moving downward? + if (dy > 0) { + // hit a solid brick? + flags = box[2] | box[3]; + if (flags & FLAG_SOLID) { + // don't land if we are bumping against a wall + // but land if entire bottom border is solid + if (box[2] & box[3] & FLAG_SOLID) flags = FLAG_PLATFORM; + else if (box[0] & box[2] & FLAG_SOLID) { } + else if (box[1] & box[3] & FLAG_SOLID) { } + else flags = FLAG_PLATFORM; + } + // land on platform, but only if we + // transit past the lower boundary of a cell + if (flags & FLAG_PLATFORM) { + // maximum speed is 8 pixels/frame + if ((yy & 31) <= 8) { + if ((actor->yy & 31) >= 24) { + actor->yy |= 31; + actor->yvel = 0; + dy = 0; + actor->state = STANDING; + } + } + } + } + break; + case STANDING: + // if either bottom corner is empty, fall down + if ((box[2] | box[3]) == 0) { + actor->state = JUMPING; + } + // climbing a ladder? + else if (cmd_dy) { + // look at top corners if going up, bottom corners if down + flags = cmd_dy < 0 ? box[0] & box[1] : box[2] & box[3]; + if (flags & FLAG_LADDER) { + actor->state = CLIMBING; + } else { + dy = 0; + } + } + break; + case CLIMBING: + // any flags set on bottom corners? + flags = box[2] & box[3]; + if (!(flags & FLAG_LADDER)) { + // top of ladder, stand up + if (dy < 0) { + actor->state = STANDING; + } else { + // bottom of ladder, don't go thru floor + actor->state = JUMPING; + dy = 0; + } + } + break; + } + } + // update position and tile coordinate + // unless we zeroed out the velocity + if (dx) actor->xx += dx; + if (dy) actor->yy += dy; +} + +void control_actor(register Actor* actor, byte joy) { + sbyte dx = 0; + sbyte dy = 0; + sbyte speed = 1; + ActorState state = actor->state; + // jump button + if (JOY_BTN_1(joy) && state == STANDING) { + actor->yvel = JUMP_VELOCITY; + actor->state = state = JUMPING; + framecount = 0; // TODO? + } + // update position based on x/y velocity + if (actor->xvel) { + dx += actor->xvel >> 3; + if (framemask & velocity_bitmasks[actor->xvel & 7]) { + dx++; + } + } + if (actor->yvel) { + dy += actor->yvel >> 3; + if (framemask & velocity_bitmasks[actor->yvel & 7]) { + dy++; + } + } + // apply gravity when jumping or falling + if (state == JUMPING && + actor->yvel < MAX_FALL_VELOCITY) { + actor->yvel += 2; + } + // arrow keys give left/right velocity + if (JOY_LEFT(joy)) { + actor->faceleft = true; + if (actor->xvel > -MAX_HORIZ_VELOCITY) + actor->xvel -= speed; + } else if (JOY_RIGHT(joy)) { + actor->faceleft = false; + if (actor->xvel < MAX_HORIZ_VELOCITY) + actor->xvel += speed; + } else { + // slow down actor to a stop, horizontally + if (actor->xvel) actor->xvel /= 2; + } + // climb ladder? + if (state == STANDING || state == CLIMBING) { + if (JOY_UP(joy)) { dy = -1; } + if (JOY_DOWN(joy)) { dy = 1; } + } + // move sprite + if (dx | dy) { + move_actor(actor, dx, dy); + } +} + +void camera_follow(register Actor* actor) { + int dx, dy; + byte moving = actor->xvel || actor->yvel; + // compute distance between player and camera + dx = CAMERA_OFFSET_X - pixofs_x - actor->xx; + dy = CAMERA_OFFSET_Y - pixofs_y - actor->yy; + // if we are moving, scroll only when distance + // from center is greater + if (moving) { + if (dx < -CAMERA_MAX_DX) dx += CAMERA_MAX_DX; + else if (dx > CAMERA_MAX_DX) dx -= CAMERA_MAX_DX; + else dx = 0; + if (dy < -CAMERA_MAX_DY) dy += CAMERA_MAX_DY; + else if (dy > CAMERA_MAX_DY) dy -= CAMERA_MAX_DY; + else dy = 0; + } + // divide dx and dy by 16 + dx >>= 4; + dy >>= 4; + // do we need to scroll? + if (dx || dy) { + // what direction? + byte dir = 0; + if (dx < 0) dir |= SCROLL_LEFT; + if (dx > 0) dir |= SCROLL_RIGHT; + if (dy < 0) dir |= SCROLL_UP; + if (dy > 0) dir |= SCROLL_DOWN; + // start the scroll (ignored if one is already going) + scroll_start(dir); + // fast 8-pixel scroll if screen is moving too fast + if (moving && (abs(dx) >= 4 || abs(dy) >= 4)) { + scroll_finish(); + } + } +} + +void control_enemy(struct Actor* enemy) { + byte control = 0; + int pdx = player->xx - enemy->xx; + int pdy = player->yy - enemy->yy; + if (pdy > 0) { + control |= JOY_DOWN_MASK; + } else if (pdy < 0) { + control |= JOY_UP_MASK; + } + if (pdx < -32) { + control |= JOY_LEFT_MASK; + } else if (pdx > 32) { + control |= JOY_RIGHT_MASK; + } + control_actor(enemy, control); +} + +void next_frame() { + char joy; + // increment frame counter + framemask = BITMASKS[++framecount & 7]; + // get joystick bits + joy = joy_read(0); + // move player + control_actor(player, joy); + // move enemy + control_enemy(&actors[1]); + // move the camera if needed + camera_follow(player); + // animate sprites in shadow sprite ram + draw_actor(&actors[0], 0); + draw_actor(&actors[1], 1); + // wait for vblank + wait_vblank(); + // then update sprite registers + sprite_update(visbuf); + // do scrolling stuff each frame + scroll_update(); +} + +void setup_sprites(void) { + sprite_clear(); + sprite_set_shapes(SPRITE_DATA, 240, NUM_SPRITE_PATTERNS); + sprshad.spr_color[0] = COLOR_WHITE; + sprshad.spr_color[1] = COLOR_LIGHTRED; + sprshad.spr_mcolor = 0xff; + VIC.spr_mcolor0 = 12; + VIC.spr_mcolor1 = 14; +} + +void setup_charset() { + // multicolor character mode + VIC.ctrl2 |= 0x10; + VIC.bgcolor0 = 6; + VIC.bgcolor1 = 0; + VIC.bgcolor2 = 1; + + // select character set @ 0x8800 + VIC.addr = 0x12; + memcpy((char*)0x8800, charset_data, 0x800); +} + +void main(void) { + + clrscr(); + + // setup scrolling library + scroll_setup(); + + // setup the character set for the level + setup_charset(); + + // copy sprites to VIC bank + setup_sprites(); + + // build cache for actor-level collisions + build_tile_flag_map(); + + // install the joystick driver + joy_install (joy_static_stddrv); + + // repaint screen memory w/ the map + scroll_refresh(); + + player->xx = 3*32+8; + player->yy = 2*32+8-16; + + player->xx = 0; + player->yy = 31; + player->state = STANDING; + /* + player->xx = 32; + player->yy = 0; + player->xx = 33; + player->yy = 100; + player->state = JUMPING; + */ +// actor_set_position(player, 63, 63, STANDING); + actors[1].xx = 128; + + // infinite loop + while (1) { + next_frame(); + } +} diff --git a/presets/c64/hello.dasm b/presets/c64/hello.dasm deleted file mode 100644 index 906aa2c2..00000000 --- a/presets/c64/hello.dasm +++ /dev/null @@ -1,40 +0,0 @@ - - include "cartheader.dasm" - -; program start -Temp equ $03 -Start - sei ; turn off interrupts - ldy #0 - sty $d020 ; reset border color -Loop - lda Message,y ; load message byte - beq EOM ; 0 = end of string - clc - adc #$c0 ; + 192 - sta $400+41,y ; store to screen - iny - bne Loop ; next character -EOM -Wait1 - lda $d011 - bmi Wait1 ; wait for line < 256 -Wait2 - lda $d012 ; get current scanline -Wait3 - cmp $d012 - beq Wait3 ; wait for scanline to change - lsr - lsr - clc - adc Temp - sta $d020 ; set border color - lda $d011 ; get status bits - bpl Wait2 ; repeat until line >= 256 - sty $d020 ; reset border color - dec Temp ; scroll colors - jmp Wait1 ; endless loop -Message - ; PETSCII - http://sta.c64.org/cbm64pet.html - byte "HELLO`WORLDa" - byte 0 diff --git a/presets/c64/joygame.c b/presets/c64/joygame.c new file mode 100644 index 00000000..bb8ed422 --- /dev/null +++ b/presets/c64/joygame.c @@ -0,0 +1,131 @@ + +#include "common.h" +//#link "common.c" + +#include "sprites.h" +//#link "sprites.c" + +#define NUM_SPRITES 3 + +/*{w:12,h:21,bpp:2,brev:1,wpimg:64,count:3,aspect:2}*/ +const char SPRITE_MC_DATA[64*NUM_SPRITES] = { + 0x0A,0xAA,0x80,0x0A,0xAA,0x80,0x2A,0xAA, + 0xA0,0x2A,0xAA,0xA0,0xAA,0xAA,0xAA,0xFF, + 0xD5,0x40,0x0D,0xD7,0x40,0x3D,0xD5,0x54, + 0x37,0x55,0x54,0x37,0x55,0x54,0x35,0x55, + 0x00,0x3A,0xA0,0x00,0xEA,0xA8,0x00,0xAB, + 0xAA,0x00,0xAB,0xAA,0x00,0xAB,0xAA,0x80, + 0xAA,0xEA,0x80,0xAA,0xAA,0x80,0x0F,0xFC, + 0x00,0x0F,0xFC,0x00,0x0F,0xFF,0xC0,0x00, + + 0x02,0xAA,0xA0,0x02,0xAA,0xA0,0x0A,0xAA, + 0xA8,0x0A,0xAA,0xA8,0xAA,0xAA,0xAA,0x01, + 0x57,0xFF,0x01,0xD7,0x70,0x15,0x57,0x7C, + 0x15,0x55,0xDC,0x15,0x55,0xDC,0x00,0x55, + 0x5C,0x00,0x0A,0xAC,0x00,0x2A,0xAB,0x00, + 0xAA,0xEA,0x00,0xAA,0xEA,0x02,0xAA,0xEA, + 0x02,0xAB,0xAA,0x02,0xAA,0xAA,0x00,0x3F, + 0xF0,0x00,0x3F,0xF0,0x03,0xFF,0xF0,0x00, + + 0x00,0xAA,0x80,0x02,0xAA,0xA0,0x0A,0xAA,0xA8, + 0x0A,0xAE,0xA8,0x0A,0xBB,0xA8,0x0A,0xBA,0xA8, + 0x0A,0xBB,0xA8,0x0A,0xAE,0xA8,0x0A,0xAA,0xA8, + 0x09,0xAA,0x98,0x08,0x6A,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, + 0x80, +}; + +// starting index for sprites +#define SPRITE_SHAPE 192 + +// player data +int player_x = 172; +byte player_y = 145; +byte faceleft = 0; // 0 = face right, 1 = face left + +void init_sprite_shapes(void) { + byte i; + for (i=0; i -#include -#include -#include -#include -#include +#include "common.h" -/*{w:24,h:21,bpp:1,brev:1}*/ -const char sprite[3*21] = { - 0x00,0x7F,0x00,0x01,0xFF,0xC0,0x03,0xFF,0xE0, - 0x03,0xE7,0xE0,0x07,0xD9,0xF0,0x07,0xDF,0xF0, - 0x07,0xD9,0xF0,0x03,0xE7,0xE0,0x03,0xFF,0xE0, - 0x03,0xFF,0xE0,0x02,0xFF,0xA0,0x01,0x7F,0x40, - 0x01,0x3E,0x40,0x00,0x9C,0x80,0x00,0x9C,0x80, - 0x00,0x49,0x00,0x00,0x49,0x00,0x00,0x3E,0x00, - 0x00,0x3E,0x00,0x00,0x3E,0x00,0x00,0x1C,0x00 +/*{w:24,h:21,bpp:1,brev:1,count:1}*/ +const char SPRITE_DATA[64] = { + 0x0f,0xff,0x80,0x17,0xff,0x40,0x2b,0xfe, + 0xa0,0x7f,0xff,0xf0,0xff,0xc0,0x3f,0xe0, + 0x3f,0xc0,0x17,0xbe,0xc0,0x2d,0x7f,0xf0, + 0x2b,0x7f,0xf8,0x2a,0xff,0xf8,0x15,0xf6, + 0x00,0x3f,0xf8,0x00,0xfd,0xfc,0x00,0xfd, + 0xff,0x00,0xfe,0xff,0x80,0xff,0x7f,0xc0, + 0xff,0xff,0xc0,0xff,0xff,0xc0,0x0a,0xa8, + 0x00,0x0f,0xf8,0x00,0x0f,0xff,0x80,0x03, }; -/*{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 -}; +void main(void) { + // variables + int x = 172; // sprite X position (16-bit) + byte y = 145; // sprite Y position (8-bit) + byte bgcoll; // sprite background collision flags + byte joy; // joystick flags -// Raster wait with line argument -void rasterWait(unsigned char line) { - while (VIC.rasterline < line) ; -} - -int main (void) -{ - int n; - int x,y; - char bgcoll; + // copy sprite pattern to RAM address 0x3800 + memcpy((char*)0x3800, SPRITE_DATA, sizeof(SPRITE_DATA)); + // set sprite #0 shape entry (224) + POKE(0x400 + 0x3f8 + 0, 0x3800 / 64); + // set position and color + VIC.spr_pos[0].x = 172; + VIC.spr_pos[0].y = 145; + VIC.spr_color[0] = COLOR_GREEN; + // enable sprite #0 + VIC.spr_ena = 0b00000001; + // install the joystick driver joy_install (joy_static_stddrv); - // set background color - VIC.bgcolor0 = 3; - // clear interrupts to avoid glitching - __asm__("SEI"); - // set sprite bitmap data - for (n = 0 ; n < sizeof(sprite) ; n++) { - POKE(0x340 + n, sprite[n]); - POKE(0x380 + n, spritemc[n]); - } - // 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(0x7f8, 13); - POKE(0x7f9, 14); - // set initial x/y positions - x = 160; - y = 128; - // loop + + // loop forever while (1) { // get joystick bits - char joy = joy_read(0); - // move sprite based on arrow keys - if (JOY_LEFT(joy)) --x; - if (JOY_UP(joy)) --y; - if (JOY_RIGHT(joy)) ++x; - if (JOY_DOWN(joy)) ++y; - // set VIC registers based on position - VIC.spr0_x = x; - 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 - bgcoll = VIC.spr_bg_coll; - VIC.spr0_color = (bgcoll & 1) ? 10 : 0; - VIC.spr1_color = (bgcoll & 2) ? 10 : 0; + joy = joy_read(0); + // move sprite based on joystick + if (JOY_LEFT(joy)) { x -= 1; } // move left 1 pixel + if (JOY_RIGHT(joy)) { x += 1; } // move right 1 pixel + if (JOY_UP(joy)) { y -= 1; } // move up 1 pixel + if (JOY_DOWN(joy)) { y += 1; } // move down 1 pixel // wait for end of frame - rasterWait(255); + waitvsync(); + // set sprite registers based on position + VIC.spr_pos[0].x = x; + VIC.spr_pos[0].y = y; + // set X coordinate high bit + VIC.spr_hi_x = (x & 0x100) ? 1 : 0; + // grab and reset collision flags + bgcoll = VIC.spr_bg_coll; + // change color when we collide with background + VIC.spr_color[0] = (bgcoll & 1) ? + COLOR_LIGHTRED : COLOR_CYAN; } - // uninstall joystick driver (not really necessary) - joy_uninstall(); - return EXIT_SUCCESS; } diff --git a/presets/c64/mandel.c b/presets/c64/mandel.c index 1cfa0c36..26d99bef 100644 --- a/presets/c64/mandel.c +++ b/presets/c64/mandel.c @@ -12,18 +12,17 @@ #include "common.h" -//#link "multilines.c" - -void setup_bitmap_multi(); -void set_pixel(byte x, byte y, byte color); +//#link "mcbitmap.c" +#include "mcbitmap.h" /* Graphics definitions */ #define SCREEN_X 160 #define SCREEN_Y 192 #define MAXCOL 16 -#define maxiterations 16 +#define maxiterations 64 #define fpshift (10) +#define fpone (1<>fpshift) #define fpabs(_x) (abs(_x)) @@ -71,10 +70,11 @@ void mandelbrot (signed short x1, signed short y1, signed short x2, if (count == maxiterations) { color = (0); } else { - color = COLORS[count % MAXCOL]; + color = count < MAXCOL + ? COLORS[count] + : COLORS[MAXCOL-1]; + set_pixel(x, y, color); } - /* Set pixel */ - set_pixel(x, y, color); } } } @@ -85,11 +85,11 @@ int main (void) VIC.bgcolor0 = 0x00; /* Calc mandelbrot set */ - mandelbrot (tofp (-2), tofp (-2), tofp (2), tofp (2)); + mandelbrot (-fpone/2, -fpone, 0, -fpone/2); /* Fetch the character from the keyboard buffer and discard it */ cgetc (); - + /* Done */ return EXIT_SUCCESS; } diff --git a/presets/c64/multilines.c b/presets/c64/mcbitmap.c similarity index 70% rename from presets/c64/multilines.c rename to presets/c64/mcbitmap.c index a9980f25..bc5ec516 100644 --- a/presets/c64/multilines.c +++ b/presets/c64/mcbitmap.c @@ -1,36 +1,35 @@ -#include -#include -#include -#include -#include #include "common.h" //#link "common.c" +#include "mcbitmap.h" + void setup_bitmap_multi() { VIC.ctrl1 = 0x38; VIC.ctrl2 = 0x18; - // set VIC bank ($4000-$7FFF) - // https://www.c64-wiki.com/wiki/VIC_bank - CIA2.pra = 0x02; - // set VIC screen to $6000 - VIC.addr = 0x80; - // clear bitmap and screen RAM - memset((void*)0x4000, 0, 0x2000); - memset((void*)0xd800, 0, 40*25); + SET_VIC_BANK(MCB_BITMAP); + SET_VIC_BITMAP(MCB_BITMAP); + SET_VIC_SCREEN(MCB_COLORS); + memset((void*)MCB_BITMAP, 0, 0x2000); + memset((void*)MCB_COLORS, 0, 0x800); + memset(COLOR_RAM, 0, 40*25); } const byte PIXMASK[4] = { ~0xc0, ~0x30, ~0x0c, ~0x03 }; const byte PIXSHIFT[4] = { 6, 4, 2, 0 }; byte is_pixel(byte x, byte y) { - word ofs = ((x>>2)*8 + (y>>3)*320) | (y&7) | 0x4000; - return PEEK(ofs) & ~PIXMASK[x & 3]; + word ofs = ((x>>2)*8 + (y>>3)*320) | (y&7) | MCB_BITMAP; + byte pixvalue; + ENABLE_HIMEM(); + pixvalue = PEEK(ofs); + DISABLE_HIMEM(); + return pixvalue & ~PIXMASK[x & 3];; } void set_pixel(byte x, byte y, byte color) { word ofs,b,cram,sram; - byte ccol,scol; + byte ccol,scol,used; byte val; if (x >= 160 || y >= 192) return; @@ -40,35 +39,44 @@ void set_pixel(byte x, byte y, byte color) { if (color == VIC.bgcolor0) { val = 0; } else { + // calculate character (and color RAM) offset cram = ((x>>2) + (y>>3)*40); - sram = cram | 0x6000; + sram = cram | MCB_COLORS; cram |= 0xd800; + // read color ram, screen memory, and used bits + ENABLE_HIMEM(); ccol = PEEK(cram); scol = PEEK(sram); - // color RAM contains unused bits (0x10 and 0x20) + used = PEEK(sram | 0x400); + DISABLE_HIMEM(); // unused in lower nibble of screen RAM? (value 2) - if (color == (scol & 0xf) || !(ccol & 0x10)) { + if (color == (scol & 0xf) || !(used & 0x10)) { val = 2; scol = (scol & 0xf0) | color; - ccol |= 0x10; + used |= 0x10; POKE(sram, scol); // unused in upper nibble of screen RAM? (value 1) - } else if (color == (scol >> 4) || !(ccol & 0x20)) { + } else if (color == (scol >> 4) || !(used & 0x20)) { val = 1; scol = (scol & 0xf) | (color << 4); - ccol |= 0x20; + used |= 0x20; POKE(sram, scol); // all other colors in use, use color RAM } else { val = 3; - ccol = 0x30 | color; + used |= 0x40; + ccol = color; + POKE(cram, ccol); } - POKE(cram, ccol); + // write to unused bit + POKE(sram | 0x400, used); } - ofs = ((x>>2)*8 + (y>>3)*320) | (y&7) | 0x4000; + ofs = ((x>>2)*8 + (y>>3)*320) | (y&7) | MCB_BITMAP; x &= 3; + ENABLE_HIMEM(); b = PEEK(ofs) & PIXMASK[x]; + DISABLE_HIMEM(); if (val) { b |= val << PIXSHIFT[x]; } @@ -91,6 +99,7 @@ void draw_line(int x0, int y0, int x1, int y1, byte color) { } } +// support recursion #pragma static-locals(push,off) byte flood_fill(byte x, byte y, byte color) { register byte x1 = x; diff --git a/presets/c64/mcbitmap.h b/presets/c64/mcbitmap.h new file mode 100644 index 00000000..0e45cc36 --- /dev/null +++ b/presets/c64/mcbitmap.h @@ -0,0 +1,16 @@ + +#include "common.h" + +#define MCB_COLORS 0xc000 +#define MCB_BITMAP 0xe000 + +void setup_bitmap_multi(); + +byte is_pixel(byte x, byte y); + +void set_pixel(byte x, byte y, byte color); + +void draw_line(int x0, int y0, int x1, int y1, byte color); + +byte flood_fill(byte x, byte y, byte color); + \ No newline at end of file diff --git a/presets/c64/multisprite.ca65 b/presets/c64/multisprite.ca65 new file mode 100644 index 00000000..d75b7a3a --- /dev/null +++ b/presets/c64/multisprite.ca65 @@ -0,0 +1,205 @@ + +MAX_MSPRITES = 28 + +MIN_Y_SPACING = 35 + +DEBUG = 1 + +.code + +.global _msprite_render_init +_msprite_render_init: + lda #0 + sta j + sta k + sta $d001 ; ypos #0 + sta $d003 + sta $d005 + sta $d007 + sta $d009 + sta $d00b + sta $d00d + sta $d00f ; ypos #7 + lda #$ff + sta $d015 ; sprite enable + rts + +.global _msprite_render_section +_msprite_render_section: + lda $d012 + clc + adc #MIN_Y_SPACING + sta bailout_line +@loop: +.ifdef DEBUG + inc $d020 +.endif + lda $d012 + cmp bailout_line + bcs @loopexit + ldy k + cpy #MAX_MSPRITES + bcs @loopexit + lda _msprite_order,y ; $ff = end of sprite list + tay ; Y = sprite index from sort array + lda j + asl + tax ; X = j * 2 +; if (VIC.spr_pos[j].y >= 250) break; + lda $d001,x + cmp #250 + bcs @loopexit ; offscreen? +; if (VIC.spr_pos[j].y+22 >= VIC.rasterline) break; + clc + adc #22 + cmp $d012 ; are we done drawing + bcs @loopexit ; this sprite yet? +; VIC.spr_pos[j].y = msprite_y[i]; + lda _msprite_y,y + sta $d001,x +; VIC.spr_pos[j].x = msprite_x[i]; + lda _msprite_x_lo,y + sta $d000,x + ldx j ; X = j +; VIC.spr_color[j] = msprite_color[i]; + lda _msprite_color,y + sta $d027,x +; POKE(0x7f8+j, msprite_shape[i]); + lda _msprite_shape,y + sta $07f8,x +; set hi X bit + lda _msprite_x_hi,y + lsr + lda NOTBITS,x + and $d010 + bcc @nohix + ora BITS,x +@nohix: + sta $d010 ; update X hi bits + inc k ; next object + inx + txa + and #7 + sta j ; next h/w sprite + jmp @loop +@loopexit: +.ifdef DEBUG + lda #0 + sta $d020 +.endif + rts + +; http://selmiak.bplaced.net/games/c64/index.php?lang=eng&game=Tutorials&page=Sprite-Multiplexing +.global _msprite_sort +_msprite_sort: + ldx #$00 +@sortloop: + ldy _msprite_order+1,x + lda _msprite_y,y + ldy _msprite_order,x + cmp _msprite_y,y + bcs @sortskip + stx @sortreload+1 +@sortswap: + lda _msprite_order+1,x + sta _msprite_order,x + tya + sta _msprite_order+1,x + cpx #$00 + beq @sortreload + dex + ldy _msprite_order+1,x + lda _msprite_y,y + ldy _msprite_order,x + cmp _msprite_y,y + bcc @sortswap +@sortreload: + ldx #$00 ; self-modifying code +@sortskip: + inx + cpx #MAX_MSPRITES-1 + bcc @sortloop + rts + +.global _msprite_add_velocity +_msprite_add_velocity: + tay + dey +@loop: + lda _msprite_y_frac,y + clc + adc _msprite_yvel_lo,y + sta _msprite_y_frac,y + lda _msprite_y,y + adc _msprite_yvel_hi,y + sta _msprite_y,y + lda _msprite_x_frac,y + clc + adc _msprite_xvel_lo,y + sta _msprite_x_frac,y + lda _msprite_xvel_hi,y + bmi @xneg + lda _msprite_x_lo,y + adc _msprite_xvel_hi,y + sta _msprite_x_lo,y + lda _msprite_x_hi,y + adc #0 + sta _msprite_x_hi,y + dey + bpl @loop + rts +@xneg: + lda _msprite_x_lo,y + adc _msprite_xvel_hi,y + sta _msprite_x_lo,y + lda _msprite_x_hi,y + adc #$ff + sta _msprite_x_hi,y + dey + bpl @loop + rts + +BITS: + .byte $01,$02,$04,$08,$10,$20,$40,$80 +NOTBITS: + .byte $FE,$FD,$FB,$F7,$EF,$DF,$BF,$7F + +;;;;; + +.data + +j: .res 1 ; h/w sprite index +k: .res 1 ; object index +bailout_line: .res 1 + +.global _msprite_order +.global _msprite_x_lo +.global _msprite_x_hi +.global _msprite_y +.global _msprite_color +.global _msprite_shape +.global _msprite_flags +.global _msprite_last_y + +_msprite_order: .res MAX_MSPRITES +_msprite_x_lo: .res MAX_MSPRITES +_msprite_x_hi: .res MAX_MSPRITES +_msprite_y: .res MAX_MSPRITES +_msprite_color: .res MAX_MSPRITES +_msprite_shape: .res MAX_MSPRITES +_msprite_flags: .res MAX_MSPRITES +_msprite_last_y:.res 1 + +.global _msprite_x_frac +.global _msprite_xvel_lo +.global _msprite_xvel_hi +.global _msprite_y_frac +.global _msprite_yvel_lo +.global _msprite_yvel_hi + +_msprite_x_frac: .res MAX_MSPRITES +_msprite_xvel_lo: .res MAX_MSPRITES +_msprite_xvel_hi: .res MAX_MSPRITES +_msprite_y_frac: .res MAX_MSPRITES +_msprite_yvel_lo: .res MAX_MSPRITES +_msprite_yvel_hi: .res MAX_MSPRITES diff --git a/presets/c64/musicplayer.c b/presets/c64/musicplayer.c index 267a63dd..43debf06 100644 --- a/presets/c64/musicplayer.c +++ b/presets/c64/musicplayer.c @@ -3,10 +3,6 @@ A simple music player. */ -#include -#include -#include <_sid.h> - #include "common.h" //#link "common.c" @@ -20,9 +16,7 @@ const int note_table_pal[96] = { // SID utility routines void sid_init() { - SID.flt_freq = 0xff; - SID.flt_ctrl = 0xff; - SID.amp = 0xff; + SID.amp = 0x0f; // volume 15, no filters } #define SID_PULSE_DECAY(voice, period) \ diff --git a/presets/c64/rasterirq.ca65 b/presets/c64/rasterirq.ca65 new file mode 100644 index 00000000..55519b5b --- /dev/null +++ b/presets/c64/rasterirq.ca65 @@ -0,0 +1,92 @@ + +USE_INTERRUPTOR = 0 + +.segment "DATA" + +StartDlist: .word NullDlist-1 +NextDlist: .word NullDlist-1 + +.segment "CODE" + +.global ___dlist_setup +.global DLIST_IRQ_NEXT +.global DLIST_IRQ_RESTART +.if USE_INTERRUPTOR +.interruptor DLIST_IRQ +.endif + +___dlist_setup: + SEI ; set interrupt bit, make the CPU ignore interrupt requests + + sta StartDlist+0 ; save XA as pointer to start of dlist + stx StartDlist+1 + + LDA #%01111111 ; switch off interrupt signals from CIA-1 + STA $DC0D + + AND $D011 ; clear most significant bit of VIC's raster register + STA $D011 + + LDA $DC0D ; acknowledge pending interrupts from CIA-1 + LDA $DD0D ; acknowledge pending interrupts from CIA-2 + + LDA #252 ; set rasterline where interrupt shall occur + STA $D012 + +.if !USE_INTERRUPTOR + LDA #DLIST_IRQ + STA $0315 +.endif + + LDA #%00000001 ; enable raster interrupt signals from VIC + STA $D01A + cli + rts + +DLIST_IRQ: +DLIST_CALL: + lda NextDlist+1 + pha + lda NextDlist+0 + pha + rts + +DLIST_IRQ_RESTART: + sta $d012 + lda StartDlist+0 + sta NextDlist+0 + lda StartDlist+1 + sta NextDlist+1 + bne DLIST_ACK + +DLIST_IRQ_STOP: + lda #0 ; disable raster interrupt signals from VIC + sta $D01A + bne DLIST_ACK + +DLIST_IRQ_NEXT: + sta $d012 + pla + sta NextDlist+0 + pla + sta NextDlist+1 +DLIST_ACK: + ASL $D019 ; acknowledge the interrupt by clearing the VIC's interrupt flag +.if USE_INTERRUPTOR + clc + rts +.else + pla + tay + pla + tax + pla + rti ; return from interrupt +; JMP $EA31 ; jump into KERNAL's standard interrupt service routine to handle keyboard scan, cursor display etc. +.endif + +NullDlist: + lda #252 + jmp DLIST_IRQ_RESTART diff --git a/presets/c64/rasterirq.h b/presets/c64/rasterirq.h new file mode 100644 index 00000000..073873ce --- /dev/null +++ b/presets/c64/rasterirq.h @@ -0,0 +1,23 @@ +#ifndef _RASTERIRQ_H +#define _RASTERIRQ_H + + +// internal function, use macro instead +void __dlist_setup(void* ptr); + +// initialize display list with function 'func' +#define DLIST_SETUP(func) \ + __dlist_setup(((char*)func)-1) + +// continue on line 'line' +#define DLIST_NEXT(line) \ + __A__ = line; \ + asm ("jsr DLIST_IRQ_NEXT"); + +// restart display list on line 'line' +#define DLIST_RESTART(line) \ + __A__ = line; \ + asm ("jmp DLIST_IRQ_RESTART"); + + +#endif diff --git a/presets/c64/scroll1.c b/presets/c64/scroll1.c index 7541250f..c4b62f4f 100644 --- a/presets/c64/scroll1.c +++ b/presets/c64/scroll1.c @@ -1,48 +1,72 @@ -#include -#include -#include +#include "common.h" +//#link "common.c" + #include -#include -#include -typedef unsigned char byte; -typedef unsigned short word; +#define SCROLL_TOP 0 +#define SCROLL_ROWS 10 -void rasterWait(unsigned char line) { - while (VIC.rasterline < line) ; +byte scroll_x = 0; // x scroll position + +// return the next character on the right side +// for a given row +char get_char_for_row(byte row) { + return '0' + row; } -byte x = 0; // x scroll position -byte y = 0; // y scroll position -byte* scrnbuf; // screen buffer +void draw_right_column() { + // get the top-right corner address of scroll area + word addr = SCRNADR(0x400, 39, SCROLL_TOP); + byte row; + // draw one character per row + for (row=0; row -#include -#include +#include "common.h" +//#link "common.c" + #include -#include -#include -typedef unsigned char byte; -typedef unsigned short word; +#define BUFFER_0 0x400 +#define BUFFER_1 0x3c00 -void rasterWait(unsigned char line) { - while (VIC.rasterline < line) ; -} +byte scroll_x = 0; // x scroll position +byte* scrnbuf[2]; // screen buffer(s) +byte visbuf; // which buffer is visible? 0 or 1 -byte x = 0; // x scroll position -byte y = 0; // y scroll position -byte* scrnbuf[2]; // screen buffer(s) -byte frame = 0; - -void main(void) { +void scroll_one_pixel_left() { byte* src; byte* dst; - + // scroll left + scroll_x--; + // set scroll registers + SET_SCROLL_X(scroll_x); + // calculate frame pointers + // source = visible buffer + src = scrnbuf[visbuf] + (scroll_x & 7) * 128; + // destination = hidden buffer + dst = scrnbuf[visbuf ^ 1] + (scroll_x & 7) * 128; + // wait for vsync + waitvsync(); + // scroll hidden buffer + memcpy(dst, src+1, 128); + // every 8 pixels, switch visible and hidden buffers + // and set VIC to point to other buffer + if ((scroll_x & 7) == 0) { + visbuf ^= 1; + SET_VIC_SCREEN(visbuf ? BUFFER_1 : BUFFER_0); + } +} + +void main(void) { clrscr(); - printf("\r\n\r\n\r\n Hello World!"); - printf("\r\n\r\n\r\n This is how we scroll"); - printf("\r\n\r\n\r\n One line at a time"); - printf("\r\n\r\n\r\n And now we have two buffers"); - printf("\r\n\r\n\r\n To copy all the bytes "); + printf("\n\n\n Hello Scrolling World!"); + printf("\n\n\n We scroll in one direction"); + printf("\n\n\n And now we have two buffers"); + printf("\n\n\n And copy hidden -> visible"); + printf("\n\n\n 128 bytes (of 1024) per frame"); + VIC.ctrl1 = 0x10; // 24 lines VIC.ctrl2 = 0x00; // 38 columns - + VIC.bordercolor = COLOR_GRAY1; + // get screen buffer addresses - scrnbuf[0] = (byte*) 0x400; - scrnbuf[1] = (byte*) 0x3c00; - memcpy(scrnbuf[1], scrnbuf[0], 24*40); + scrnbuf[0] = (byte*) BUFFER_0; + scrnbuf[1] = (byte*) BUFFER_1; // infinite loop while (1) { - // scroll left - x--; - // set scroll registers - VIC.ctrl1 = VIC.ctrl1 & 0xf8; - VIC.ctrl1 |= (y & 7); - VIC.ctrl2 = VIC.ctrl2 & 0xf8; - VIC.ctrl2 |= (x & 7); - // calculate frame pointers - src = scrnbuf[frame&1] + (x&7)*128; - dst = scrnbuf[frame&1^1] + (x&7)*128; - // wait for vsync - rasterWait(255); - // scroll hidden buffer - memcpy(dst, src+1, 128); - // every 8 pixels, switch farmes - if ((x & 7) == 0) { - VIC.addr = (VIC.addr & 0xf) | ((frame & 1) ? 0x10 : 0xf0); - frame++; - } + scroll_one_pixel_left(); } } diff --git a/presets/c64/scroll3.c b/presets/c64/scroll3.c index dcbf6091..c074c58a 100644 --- a/presets/c64/scroll3.c +++ b/presets/c64/scroll3.c @@ -1,66 +1,45 @@ -#include -#include -#include +#include "common.h" +//#link "common.c" + #include -#include -#include -#include -#include - -typedef uint8_t byte; -typedef uint16_t word; -typedef int8_t sbyte; - -#define COLS 40 -#define ROWS 25 - -void raster_wait(unsigned char line) { - while (VIC.rasterline < line) ; -} - -void wait_vblank() { - raster_wait(255); // TODO -} sbyte scroll_fine_x = 0; sbyte scroll_fine_y = 0; -byte curbuf = 0; byte* scrnbuf[2]; // screen buffer(s) +byte hidbuf = 0; void scroll_update_regs() { - VIC.ctrl1 = (VIC.ctrl1 & 0xf8) | scroll_fine_y; - VIC.ctrl2 = (VIC.ctrl2 & 0xf8) | scroll_fine_x; + SET_SCROLL_X(scroll_fine_x); + SET_SCROLL_Y(scroll_fine_y); } void scroll_swap() { // swap hidden and visible buffers - curbuf ^= 1; + hidbuf ^= 1; // wait for vblank and update registers - wait_vblank(); + waitvsync(); scroll_update_regs(); - VIC.addr = (VIC.addr & 0xf) | (curbuf ? 0x00 : 0x10); - // copy visible buffer to hidden buffer - memcpy(scrnbuf[curbuf], scrnbuf[curbuf^1], COLS*ROWS); + SET_VIC_SCREEN(hidbuf ? 0x8000 : 0x8400); } void scroll_left() { - memcpy(scrnbuf[curbuf], scrnbuf[curbuf^1]+1, COLS*ROWS-1); + memcpy(scrnbuf[hidbuf], scrnbuf[hidbuf^1]+1, COLS*ROWS-1); scroll_swap(); } void scroll_right() { - memcpy(scrnbuf[curbuf]+1, scrnbuf[curbuf^1], COLS*ROWS-1); + memcpy(scrnbuf[hidbuf]+1, scrnbuf[hidbuf^1], COLS*ROWS-1); scroll_swap(); } void scroll_up() { - memcpy(scrnbuf[curbuf], scrnbuf[curbuf^1]+COLS, COLS*(ROWS-1)); + memcpy(scrnbuf[hidbuf], scrnbuf[hidbuf^1]+COLS, COLS*(ROWS-1)); scroll_swap(); } void scroll_down() { - memcpy(scrnbuf[curbuf]+COLS, scrnbuf[curbuf^1], COLS*(ROWS-1)); + memcpy(scrnbuf[hidbuf]+COLS, scrnbuf[hidbuf^1], COLS*(ROWS-1)); scroll_swap(); } @@ -92,29 +71,32 @@ void scroll_setup() { // get screen buffer addresses scrnbuf[0] = (byte*) 0x8000; scrnbuf[1] = (byte*) 0x8400; + // copy existing text to screen 0 memcpy(scrnbuf[0], (byte*)0x400, COLS*ROWS); // copy screen 1 to screen 0 memcpy(scrnbuf[1], scrnbuf[0], COLS*ROWS); - // set VIC bank ($4000-$7FFF) + // set VIC bank // https://www.c64-wiki.com/wiki/VIC_bank - CIA2.pra = 0x01; - - VIC.ctrl1 = 0x10; // 24 lines - VIC.ctrl2 = 0x00; // 38 columns + SET_VIC_BANK(0x8000); + + VIC.ctrl1 &= ~0x08; // 24 lines + VIC.ctrl2 &= ~0x08; // 38 columns } void main(void) { clrscr(); - printf("\r\n\r\n\r\n Hello World!"); - printf("\r\n\r\n\r\n This is how we scroll"); - printf("\r\n\r\n\r\n One line at a time"); - printf("\r\n\r\n\r\n And now we have two buffers"); - printf("\r\n\r\n\r\n To copy all the bytes "); + printf("\n\n\n Hello World!"); + printf("\n\n\n This is how we scroll"); + printf("\n\n\n And now we have two buffers"); + printf("\n\n\n And can go in any direction"); + printf("\n\n\n But we are not incremental"); + printf("\n\n\n And have to pause to scroll and copy"); scroll_setup(); + VIC.bordercolor = COLOR_GRAY1; // install the joystick driver joy_install (joy_static_stddrv); @@ -129,7 +111,7 @@ void main(void) { if (JOY_RIGHT(joy)) scroll_horiz(1); if (JOY_DOWN(joy)) scroll_vert(1); // update regs - wait_vblank(); + waitvsync(); scroll_update_regs(); } } diff --git a/presets/c64/scroll4.c b/presets/c64/scroll4.c index 846b0361..efbc583e 100644 --- a/presets/c64/scroll4.c +++ b/presets/c64/scroll4.c @@ -1,190 +1,94 @@ -#include -#include -#include +#include "common.h" +//#link "common.c" + +#include "scrolling.h" +//#link "scrolling.c" + +#include "sprites.h" +//#link "sprites.c" + #include -#include -#include -#include -#include -typedef uint8_t byte; -typedef uint16_t word; -typedef int8_t sbyte; - -#define COLS 40 -#define ROWS 25 - -void raster_wait(unsigned char line) { - while (VIC.rasterline < line) ; -} - -void wait_vblank() { - raster_wait(255); // TODO -} - -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]; - -void draw_cell(byte x, byte y) { +static void draw_cell(word ofs, byte x, byte y) { byte xx = x + origin_x; byte yy = y + origin_y; byte ch = xx ^ yy; - word ofs = x+y*COLS; - scrnbuf[curbuf][ofs] = ch; // character - tempbuf[ofs] = ch; // color + hidbuf[ofs] = ch; // character + colorbuf[ofs] = ch; // color } void scroll_draw_column(byte col) { byte y; + word ofs = col; for (y=0; y