Merge branch

This commit is contained in:
Steven Hugg 2022-08-30 19:50:37 -05:00
commit a86e554336
69 changed files with 3714 additions and 732 deletions

View File

@ -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 {

80
presets/c64/23matches.c Normal file
View File

@ -0,0 +1,80 @@
#include "common.h"
#include <cbm_petscii_charmap.h>
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();
}

Binary file not shown.

View File

@ -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

View File

@ -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;
}
}

View File

@ -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);

49
presets/c64/c64-sid.cfg Normal file
View File

@ -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__;
}

View File

@ -1,24 +1,31 @@
#include <stdlib.h>
#include <string.h>
#include <c64.h>
#include <joystick.h>
#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; i<MAX_ACTORS; i++)
draw_actor(i);
animate_explosion();
@ -541,6 +549,7 @@ void move_actor(struct Actor* actor, byte joystick, bool scroll) {
actor->yvel = 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<NUM_SPRITE_PATTERNS; i++) {
sprite_shape(hidbuf, 32+i, SPRITE_DATA[i]);
}
sprite_set_shapes(SPRITE_DATA, SPRITE_SHAPE_FIRST, NUM_SPRITE_PATTERNS);
sprshad.spr_mcolor = 0xff;
sprshad.spr_mcolor0 = 0x0f;
sprshad.spr_mcolor1 = 0x00;
VIC.spr_mcolor0 = 0x0f;
VIC.spr_mcolor1 = 0x00;
// select character set 2
VIC.addr = 0x15;
// start scrolling @ bottom of level
origin_y = START_ORIGIN_Y;
// install joystick
joy_install (joy_static_stddrv);
// setup display list
DLIST_SETUP(game_displaylist);
// main game loop
while (1) {
make_floors();

View File

@ -1,10 +1,28 @@
#include "common.h"
void raster_wait(unsigned char line) {
void raster_wait(byte line) {
while (VIC.rasterline < line) ;
}
void wait_vblank(void) {
raster_wait(255);
}
static byte VIC_BANK_PAGE[4] = {
0xc0, 0x80, 0x40, 0x00
};
char* get_vic_bank_start() {
return (char*)(VIC_BANK_PAGE[CIA2.pra & 3] << 8);
}
char* get_screen_memory() {
return ((VIC.addr & 0xf0) << 6) + get_vic_bank_start();
}
char __fastcall__ poll_keyboard() {
asm("jmp $f142");
return __A__;
}

View File

@ -1,18 +1,80 @@
#ifndef _COMMON_H
#define _COMMON_H
#include <c64.h>
#include <conio.h>
#include <stdio.h>
#include <stdlib.h>
#include <stdint.h>
#include <peekpoke.h>
#include <string.h>
#include <c64.h>
#include <joystick.h>
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

View File

@ -0,0 +1,638 @@
#include <stdio.h>
#include <conio.h>
#include <c64.h>
#include <cbm_petscii_charmap.h>
#include <string.h>
#include <stdlib.h>
#include <stdint.h>
#include <joystick.h>
//#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; y<MAP_ROWS; y++) {
for (x=0; x<MAP_COLS; x++) {
if (get_cell_at(x*4, y*4))
tileflagmap[i++] = compute_tile_flags();
}
}
}
static void draw_cell(word ofs, byte scrn_x, byte scrn_y) {
byte ch, color;
if (get_cell_at(scrn_x + origin_x, scrn_y + origin_y)) {
ch = tilechar;
color = chartileset_colour_data[tileindex];
} else {
ch = DEFAULT_CHAR;
color = DEFAULT_COLOR;
}
hidbuf[ofs] = ch;
colorbuf[ofs] = color;
}
void scroll_draw_column(byte col) {
byte y;
word ofs = col;
for (y=0; y<ROWS; y++) {
draw_cell(ofs, col, y);
ofs += COLS;
}
}
void scroll_draw_row(byte row) {
byte x;
word ofs = row * COLS;
for (x=0; x<COLS; x++) {
draw_cell(ofs, x, row);
++ofs;
}
}
#define NUM_SPRITE_PATTERNS 13
/*{w:12,h:21,bpp:2,brev:1,count:13,aspect:2}*/
const char SPRITE_DATA[NUM_SPRITE_PATTERNS][64] = {
// left direction
{
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,0x2A,0x00,0x00,0xBF,0x80,
0x00,0xAE,0x80,0x00,0xAE,0x80,0x00,0xAE,0x80,
0x00,0x2A,0x00,0x00,0x15,0x02,0x20,0xAA,0xAA,
0x2A,0xBF,0xA8,0x0A,0xAA,0xA0,0x02,0xBF,0x80,
0x00,0xAA,0x80,0x00,0xFF,0xC0,0x00,0xAA,0x80,
0x00,0xAA,0x80,0x02,0xA2,0xA0,0x02,0x80,0xA0,
0x02,0x80,0xA0,0x01,0x40,0x50,0x05,0x40,0x54
},
{
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
},
// right direction
{
0x00,0x00,0x00,0x00,0x2A,0x00,0x00,0xAB,0x80,
0x00,0xAB,0x80,0x00,0xAB,0x80,0x00,0xAB,0x80,
0x05,0x2A,0x00,0x05,0x54,0x00,0x05,0xAA,0x00,
0x05,0xAA,0x00,0x05,0xAA,0x00,0x05,0xAA,0xA0,
0x05,0xAA,0x80,0x00,0xFF,0x00,0x00,0xAA,0x00,
0x00,0xAA,0x00,0x02,0x8A,0x00,0x02,0x8A,0x00,
0x02,0x88,0x00,0x01,0x45,0x40,0x01,0x45,0x40
},
{
0x00,0x00,0x00,0x00,0xA8,0x00,0x02,0xAA,0x00,
0x02,0xAA,0x00,0x02,0xAA,0x20,0x02,0xAA,0x20,
0x00,0xA8,0x20,0x00,0x54,0xA0,0x0A,0x56,0xA0,
0x2A,0x56,0x80,0x2A,0x56,0x00,0x22,0x56,0x00,
0x22,0x56,0x00,0x23,0xFF,0x00,0x02,0xAA,0x00,
0x02,0x8A,0x00,0x02,0x8A,0x00,0x02,0x85,0x00,
0x02,0x85,0x40,0x01,0x40,0x00,0x05,0x40,0x00
},
{
0x00,0x00,0x00,0x00,0x2A,0x00,0x00,0xAB,0x80,
0x00,0xAB,0x80,0x00,0xAB,0x80,0x00,0xAB,0x80,
0x05,0x2A,0x00,0x05,0x54,0x00,0x05,0xAA,0x00,
0x05,0xAA,0x08,0x05,0xAA,0xA8,0x05,0xAA,0xA0,
0x05,0xAA,0x00,0x00,0xFF,0x00,0x00,0xAA,0x00,
0x00,0xAA,0x00,0x02,0x8A,0xA0,0x16,0x8A,0xA0,
0x16,0x80,0xA0,0x16,0x00,0x54,0x10,0x00,0x54
},
{
0x00,0x00,0x00,0x00,0x2A,0x00,0x00,0xBF,0x80,
0x00,0xAE,0x80,0x00,0xAE,0x80,0x00,0xAE,0x80,
0x00,0x2A,0x00,0x20,0x15,0x00,0x2A,0xAA,0x82,
0x0A,0xBF,0xAA,0x02,0xAA,0xA8,0x00,0xBF,0xA0,
0x00,0xAA,0x80,0x00,0xFF,0xC0,0x00,0xAA,0x80,
0x00,0xAA,0x80,0x02,0xA2,0xA0,0x02,0x80,0xA0,
0x02,0x80,0xA0,0x01,0x40,0x50,0x05,0x40,0x54
},
{
0x00,0x00,0x00,0x00,0x2A,0x00,0x00,0xAB,0x80,
0x00,0xAB,0x80,0x00,0xAB,0x80,0x00,0xAB,0x80,
0x05,0x2A,0x00,0x05,0x54,0x00,0x05,0xAA,0x00,
0x06,0xAA,0x20,0x0A,0xAA,0xA0,0x09,0xAA,0x80,
0x05,0xAA,0x00,0x00,0xFF,0x00,0x00,0xAA,0x00,
0x00,0x8A,0x00,0x02,0x2A,0x00,0x02,0xA8,0x00,
0x06,0xA2,0x80,0x05,0x01,0x40,0x01,0x41,0x50
},
// explosion
{
0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x20,0x00,
0x00,0x20,0x00,0x08,0x20,0x80,0x02,0x02,0x00,
0x00,0x10,0x00,0x00,0x54,0x00,0x00,0x10,0x00,
0x02,0x02,0x00,0x08,0x20,0x80,0x00,0x20,0x00,
0x00,0x20,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,0xA8,0x00,0x02,0x02,0x00,
0x08,0x00,0x80,0x20,0x00,0x20,0x20,0x54,0x20,
0x21,0x01,0x20,0x21,0x01,0x20,0x21,0x01,0x20,
0x20,0x54,0x20,0x20,0x00,0xA0,0x08,0x00,0x80,
0x02,0x02,0x00,0x00,0xA8,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,0x00,0x00,0x00,0xA8,0x00,0x02,0x02,0x00,
0x08,0x10,0x80,0x08,0x54,0x80,0x08,0x10,0x80,
0x02,0x02,0x00,0x00,0xA8,0x00,0x00,0x00,0x00,
0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00
},
};
typedef enum {
STANDING, JUMPING, CLIMBING
} ActorState;
typedef struct Actor {
word xx;
word yy;
sbyte xvel;
sbyte yvel;
ActorState state;
bool faceleft;
} Actor;
Actor actors[MAX_ACTORS];
Actor* const player = &actors[0];
void draw_actor(register Actor* actor, byte index) {
byte shape = 240;
word xpos = actor->xx + 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();
}
}

View File

@ -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

131
presets/c64/joygame.c Normal file
View File

@ -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<NUM_SPRITES; i++) {
sprite_shape(SPRITE_SHAPE+i, SPRITE_MC_DATA+64*i);
}
}
void init_sprite_positions(void) {
byte i;
// setup sprite positions
player_x = 172;
player_y = 145;
// set random positions for non-players
// and draw them to sprite shadow buffer
srand(7);
for (i=1; i<8; i++) {
int x = rand() % (320 - 24) + 24;
byte y = rand() % (229 - 50) + 50;
sprite_draw(i, x, y, SPRITE_SHAPE + 2);
sprshad.spr_color[i] = i | 8;
}
}
void move_player(byte joy) {
// move sprite based on joystick
if (JOY_LEFT(joy)) { player_x -= 1; faceleft = 1; } // move left 1 pixel
if (JOY_RIGHT(joy)) { player_x += 1; faceleft = 0; } // move right 1 pixel
if (JOY_UP(joy)) { player_y -= 1; } // move up 1 pixel
if (JOY_DOWN(joy)) { player_y += 1; } // move down 1 pixel
// draw player into sprite shadow buffer
sprite_draw(0, player_x, player_y, SPRITE_SHAPE + faceleft);
}
void move_non_players(void) {
byte i;
// wiggle non-player sprites randomly
for (i=1; i<8; i++) {
sprshad.spr_pos[i].y += rand() & i;
}
}
void main(void) {
// variables
byte bgcoll; // sprite background collision flags
byte sprcoll; // sprite collision flags
byte joy; // joystick flags
clrscr();
// install the joystick driver
joy_install (joy_static_stddrv);
// set multicolor sprites and colors
sprshad.spr_mcolor = 0b11111111;
sprshad.spr_color[0] = SPRITE_MC_DATA[63];
VIC.spr_mcolor0 = COLOR_GRAY2;
VIC.spr_mcolor1 = COLOR_BLACK;
// setup sprites
init_sprite_shapes();
init_sprite_positions();
// loop forever
while (1) {
// wait for end of frame
waitvsync();
// update sprite registers from sprite shadow
// buffer before frame starts drawing
sprite_update(DEFAULT_SCREEN);
// get joystick bits
joy = joy_read(0);
move_player(joy);
// move other objects
move_non_players();
// grab and reset collision flags
sprcoll = VIC.spr_coll;
bgcoll = VIC.spr_bg_coll;
// change color when player collides with sprite
sprshad.spr_color[0] = (sprcoll & 1) ? 10 : 3;
}
}

View File

@ -1,94 +1,59 @@
// ported from
// https://odensskjegg.home.blog/2018/12/29/recreating-the-commodore-64-user-guide-code-samples-in-cc65-part-three-sprites/
#include <stdio.h>
#include <stdlib.h>
#include <conio.h>
#include <peekpoke.h>
#include <c64.h>
#include <joystick.h>
#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;
}

View File

@ -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 tofp(_x) ((_x)<<fpshift)
#define fromfp(_x) ((_x)>>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;
}

View File

@ -1,36 +1,35 @@
#include <stdlib.h>
#include <string.h>
#include <stdio.h>
#include <c64.h>
#include <peekpoke.h>
#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;

16
presets/c64/mcbitmap.h Normal file
View File

@ -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);

View File

@ -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

View File

@ -3,10 +3,6 @@
A simple music player.
*/
#include <string.h>
#include <c64.h>
#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) \

View File

@ -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 ; set interrupt vectors, pointing to interrupt service routine below
STA $0314
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

23
presets/c64/rasterirq.h Normal file
View File

@ -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

View File

@ -1,48 +1,72 @@
#include <stdio.h>
#include <conio.h>
#include <c64.h>
#include "common.h"
//#link "common.c"
#include <cbm_petscii_charmap.h>
#include <string.h>
#include <stdlib.h>
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<SCROLL_ROWS; row++) {
POKE(addr, get_char_for_row(row));
addr += 40;
}
}
void scroll_one_column_left() {
// copy several rows of screen memory
// backwards one byte
const word start = SCRNADR(0x400, 0, SCROLL_TOP);
const word nbytes = SCROLL_ROWS*40-1;
memcpy((byte*)start, (byte*)start+1, nbytes);
// draw the right column of characters
draw_right_column();
}
void scroll_one_pixel_left() {
// scroll left one pixel
scroll_x -= 1;
// set scroll register with lower three bits
VIC.ctrl2 = (VIC.ctrl2 & ~0b111) | (scroll_x & 0b111);
// move screen memory if the scroll register
// has just gone past 0 and wrapped to 7
if ((scroll_x & 0b111) == 0b111) {
scroll_one_column_left();
}
}
void main(void) {
clrscr();
printf("\r\n Hello World!");
printf("\r\n\r\n This is how we scroll");
printf("\r\n\r\n One line at a time");
printf("\r\n\r\n But we don't have time");
printf("\r\n\r\n To copy all the bytes ");
printf("\n Hello Scrolling World!");
printf("\n\n We change scroll registers");
printf("\n\n And move screen memory");
printf("\n\n But we don't have time");
printf("\n\n To copy all 25 rows ");
printf("\n\n In a single frame ");
VIC.ctrl1 = 0x10; // 24 lines
VIC.ctrl2 = 0x00; // 38 columns
// get screen buffer address
scrnbuf = (byte*)((VIC.addr << 6) & 0x3c00);
VIC.bordercolor = COLOR_GRAY1;
// infinite loop
while (1) {
x--;
// set scroll registers
VIC.ctrl1 = VIC.ctrl1 & 0xf8;
VIC.ctrl1 |= (y & 7);
VIC.ctrl2 = VIC.ctrl2 & 0xf8;
VIC.ctrl2 |= (x & 7);
// wait for vsync
rasterWait(255);
// every 8 pixels, move screen cells
if ((x & 7) == 0) {
memcpy(scrnbuf, scrnbuf+1, 40*8-1);
}
waitvsync();
// scroll one pixel to the left
// and move screen memory every 8 pixels
scroll_one_pixel_left();
}
}

View File

@ -1,61 +1,58 @@
#include <stdio.h>
#include <conio.h>
#include <c64.h>
#include "common.h"
//#link "common.c"
#include <cbm_petscii_charmap.h>
#include <string.h>
#include <stdlib.h>
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();
}
}

View File

@ -1,66 +1,45 @@
#include <stdio.h>
#include <conio.h>
#include <c64.h>
#include "common.h"
//#link "common.c"
#include <cbm_petscii_charmap.h>
#include <string.h>
#include <stdlib.h>
#include <stdint.h>
#include <joystick.h>
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();
}
}

View File

@ -1,190 +1,94 @@
#include <stdio.h>
#include <conio.h>
#include <c64.h>
#include "common.h"
//#link "common.c"
#include "scrolling.h"
//#link "scrolling.c"
#include "sprites.h"
//#link "sprites.c"
#include <cbm_petscii_charmap.h>
#include <string.h>
#include <stdlib.h>
#include <stdint.h>
#include <joystick.h>
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<ROWS; y++) {
draw_cell(col, y);
draw_cell(ofs, col, y);
ofs += COLS;
}
}
void scroll_draw_row(byte row) {
byte x;
word ofs = row * COLS;
for (x=0; x<COLS; x++) {
draw_cell(x, row);
draw_cell(ofs, x, row);
++ofs;
}
}
void scroll_update_regs() {
VIC.ctrl1 = (VIC.ctrl1 & 0xf8) | scroll_fine_y;
VIC.ctrl2 = (VIC.ctrl2 & 0xf8) | scroll_fine_x;
}
void scroll_swap() {
// swap hidden and visible buffers
curbuf ^= 1;
// wait for vblank and update registers
wait_vblank();
scroll_update_regs();
VIC.addr = (VIC.addr & 0xf) | (curbuf ? 0x00 : 0x10);
}
void scroll_copy() {
// copy temp buf to color ram
memcpy(COLOR_RAM, tempbuf, COLS*ROWS);
// copy visible buffer to hidden buffer
memcpy(scrnbuf[curbuf], scrnbuf[curbuf^1], COLS*ROWS);
}
// TODO: left and up can be faster, b/c we can copy color ram downward
void scroll_left() {
memcpy(scrnbuf[curbuf], scrnbuf[curbuf^1]+1, COLS*ROWS-1);
++origin_x;
memcpy(tempbuf, COLOR_RAM+1, COLS*ROWS-1);
scroll_draw_column(COLS-1);
scroll_swap();
scroll_copy();
}
void scroll_up() {
memcpy(scrnbuf[curbuf], scrnbuf[curbuf^1]+COLS, COLS*(ROWS-1));
++origin_y;
memcpy(tempbuf, COLOR_RAM+COLS, COLS*(ROWS-1));
scroll_draw_row(ROWS-1);
scroll_swap();
scroll_copy();
}
void scroll_right() {
memcpy(scrnbuf[curbuf]+1, scrnbuf[curbuf^1], COLS*ROWS-1);
--origin_x;
memcpy(tempbuf+1, COLOR_RAM, COLS*ROWS-1);
scroll_draw_column(0);
scroll_swap();
scroll_copy();
}
void scroll_down() {
memcpy(scrnbuf[curbuf]+COLS, scrnbuf[curbuf^1], COLS*(ROWS-1));
--origin_y;
memcpy(tempbuf+COLS, COLOR_RAM, COLS*(ROWS-1));
scroll_draw_row(0);
scroll_swap();
scroll_copy();
}
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() {
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;
// 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)
// https://www.c64-wiki.com/wiki/VIC_bank
CIA2.pra = 0x01;
VIC.ctrl1 = 0x10; // 24 lines
VIC.ctrl2 = 0x00; // 38 columns
}
/*{w:24,h:21,bpp:1,brev:1}*/
const char SPRITE1[3*21] = {
0x80,0x7F,0x01,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,0x80,0x1C,0x01
};
void main(void) {
sbyte n =0;
byte n = 0;
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 But color RAM can't move");
printf("\r\n\r\n\r\n So we have to use a temp buffer");
printf("\r\n\r\n\r\n And copy it just in time");
// setup scrolling library
scroll_setup();
VIC.bordercolor = 12;
// setup sprite library and copy sprite to VIC bank
sprite_clear();
sprite_set_shapes(SPRITE1, 192, 1);
// install the joystick driver
joy_install (joy_static_stddrv);
// infinite loop
while (1) {
static char speed = 1;
// get joystick bits
char joy = joy_read(0);
// speed up scrolling while button pressed
speed = JOY_BTN_1(joy) ? 2 : 1;
// move sprite based on arrow keys
if (JOY_LEFT(joy)) scroll_horiz(-1);
if (JOY_UP(joy)) scroll_vert(-1);
if (JOY_RIGHT(joy)) scroll_horiz(1);
if (JOY_DOWN(joy)) scroll_vert(1);
// update regs
wait_vblank();
scroll_update_regs();
if (JOY_LEFT(joy)) scroll_horiz(-speed);
if (JOY_UP(joy)) scroll_vert(-speed);
if (JOY_RIGHT(joy)) scroll_horiz(speed);
if (JOY_DOWN(joy)) scroll_vert(speed);
// animate sprite in shadow sprite ram
sprite_draw(0, n++, 70, 192);
sprite_draw(0, 172, 145, 192);
// wait for vblank
waitvsync();
// update scroll registers
// and swap screens if we must
scroll_update();
// then update sprite registers
sprite_update(visbuf);
}
}

View File

@ -1,13 +1,4 @@
#include <stdio.h>
#include <conio.h>
#include <c64.h>
#include <cbm_petscii_charmap.h>
#include <string.h>
#include <stdlib.h>
#include <stdint.h>
#include <joystick.h>
#include "common.h"
//#link "common.c"
@ -17,26 +8,31 @@
#include "sprites.h"
//#link "sprites.c"
static void draw_cell(byte x, byte y) {
#include <cbm_petscii_charmap.h>
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;
hidbuf[ofs] = ch; // character
colorbuf[ofs] = ch; // color
}
void scroll_draw_column(byte col) {
byte y;
word ofs = col;
for (y=0; y<ROWS; y++) {
draw_cell(col, y);
draw_cell(ofs, col, y);
ofs += COLS;
}
}
void scroll_draw_row(byte row) {
byte x;
word ofs = row * COLS;
for (x=0; x<COLS; x++) {
draw_cell(x, row);
draw_cell(ofs, x, row);
++ofs;
}
}
@ -51,43 +47,82 @@ const char SPRITE1[3*21] = {
0x00,0x3E,0x00,0x00,0x3E,0x00,0x00,0x1C,0x00
};
int playerx = 0;
int playery = 0;
int camerax = 0;
int cameray = 0;
void update_player() {
sprite_draw(0, playerx-camerax+160, playery-cameray+140, 192);
}
void camera_follow(byte moving) {
int dx, dy;
dx = camerax - playerx;
dy = cameray - playery;
if (moving && abs(dx) < 32 && abs(dy) < 32) return;
dx >>= 4;
dy >>= 4;
if (dx) {
if (dx > 8) dx = 8;
else if (dx < -8) dx = -8;
camerax -= dx;
}
if (dy) {
if (dy > 8) dy = 8;
else if (dy < -8) dy = -8;
cameray -= dy;
}
scroll_xy(dx, dy);
}
void main(void) {
byte n = 0;
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 But color RAM can't move");
printf("\r\n\r\n\r\n So we have to use a temp buffer");
printf("\r\n\r\n\r\n And copy it just in time");
printf("\r\n\r\n\r\n Use the joystick to move");
printf("\r\n\r\n\r\n And the camera will follow");
// setup scrolling library
scroll_setup();
VIC.bordercolor = 12;
// setup sprite library and copy sprite to VIC bank
sprite_clear();
sprite_shape(hidbuf, 32, SPRITE1);
sprite_set_shapes(SPRITE1, 192, 1);
sprshad.spr_color[0] = 13;
// install the joystick driver
joy_install (joy_static_stddrv);
// infinite loop
while (1) {
while (1) {
static char speed;
static char joy;
static bool slowframe = false;
// get joystick bits
char joy = joy_read(0);
joy = joy_read(0);
// speed up scrolling while button pressed
speed = JOY_BTN_1(joy) ? 3 : 1;
// if we copied screen memory last frame,
// double speed of player for this frame
if (slowframe) speed *= 2;
// move sprite based on arrow keys
if (JOY_LEFT(joy)) scroll_horiz(-1);
if (JOY_UP(joy)) scroll_vert(-1);
if (JOY_RIGHT(joy)) scroll_horiz(1);
if (JOY_DOWN(joy)) scroll_vert(1);
if (JOY_LEFT(joy)) playerx -= speed;
if (JOY_RIGHT(joy)) playerx += speed;
if (JOY_UP(joy)) playery -= speed;
if (JOY_DOWN(joy)) playery += speed;
// move the camera?
camera_follow(joy);
slowframe = swap_needed;
// animate sprite in shadow sprite ram
sprite_draw(0, n++, 70, 32);
// wait for vblank
wait_vblank();
update_player();
// wait for end of frame
waitvsync();
// then update sprite registers
sprite_update(visbuf);
// update scroll registers
// and swap screens if we must
scroll_update();
// then update sprite registers
sprite_update(visbuf);
}
}

View File

@ -11,53 +11,123 @@ byte* hidbuf;
byte* visbuf;
byte colorbuf[COLS*ROWS];
byte swap_needed;
byte copy_needed;
//
void scroll_swap(void) {
void scroll_swap() {
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;
// set VIC bank address
SET_VIC_SCREEN((word)visbuf);
}
void scroll_copy(void) {
// copy temp buf to color ram
void copy_color_ram_slow() {
memcpy(COLOR_RAM, colorbuf, COLS*ROWS);
// copy visible buffer to hidden buffer
}
void copy_color_ram_fast() {
// fast copy loop for upper 1/2 of color ram
asm("ldy #0");
asm("@loop:");
asm("lda %v,y", colorbuf);
asm("sta $d800,y");
asm("lda %v + $100,y", colorbuf);
asm("sta $d900,y");
asm("iny");
asm("bne @loop");
// second loop for lower 1/2 of color ram
asm("@loop2:");
asm("lda %v + $200,y", colorbuf);
asm("sta $da00,y");
asm("lda %v + $300,y", colorbuf);
asm("sta $db00,y");
asm("@skip: iny");
asm("bne @loop2");
}
void copy_to_hidden_buffer_slow() {
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;
void copy_to_hidden_buffer_fast() {
// self-modifying code
asm("ldy %v+1", visbuf);
asm("sty @loop+2+6*0");
asm("iny");
asm("sty @loop+2+6*1");
asm("iny");
asm("sty @loop+2+6*2");
asm("iny");
asm("sty @skip-1-3");
asm("ldy %v+1", hidbuf);
asm("sty @loop+5+6*0");
asm("iny");
asm("sty @loop+5+6*1");
asm("iny");
asm("sty @loop+5+6*2");
asm("iny");
asm("sty @skip-1");
// fast copy loop
asm("ldy #0");
asm("@loop:");
asm("lda $8000,y");
asm("sta $8000,y");
asm("lda $8100,y");
asm("sta $8100,y");
asm("lda $8200,y");
asm("sta $8200,y");
asm("cpy #$e8");
asm("bcs @skip");
asm("lda $8300,y");
asm("sta $8300,y");
asm("@skip: iny");
asm("bne @loop");
}
void copy_if_needed() {
if (copy_needed) {
copy_to_hidden_buffer_fast();
copy_needed = false;
}
}
void scroll_left(void) {
memcpy(hidbuf, hidbuf+1, COLS*ROWS-1);
memcpy(colorbuf, colorbuf+1, COLS*ROWS-1);
void scroll_update() {
SET_SCROLL_X(scroll_fine_x);
SET_SCROLL_Y(scroll_fine_y);
if (swap_needed) {
scroll_swap();
copy_color_ram_fast();
swap_needed = false;
copy_needed = true;
} else {
copy_if_needed();
}
}
static void scroll_left() {
copy_if_needed();
memmove(hidbuf, hidbuf+1, COLS*ROWS-1);
memmove(colorbuf, colorbuf+1, COLS*ROWS-1);
++origin_x;
scroll_draw_column(COLS-1);
swap_needed = true;
}
void scroll_up(void) {
memcpy(hidbuf, hidbuf+COLS, COLS*(ROWS-1));
memcpy(colorbuf, colorbuf+COLS, COLS*(ROWS-1));
static void scroll_up() {
copy_if_needed();
memmove(hidbuf, hidbuf+COLS, COLS*(ROWS-1));
memmove(colorbuf, colorbuf+COLS, COLS*(ROWS-1));
++origin_y;
scroll_draw_row(ROWS-1);
swap_needed = true;
}
void scroll_right(void) {
static void scroll_right() {
copy_if_needed();
memmove(hidbuf+1, hidbuf, COLS*ROWS-1);
memmove(colorbuf+1, colorbuf, COLS*ROWS-1);
--origin_x;
@ -65,7 +135,8 @@ void scroll_right(void) {
swap_needed = true;
}
void scroll_down(void) {
static void scroll_down() {
copy_if_needed();
memmove(hidbuf+COLS, hidbuf, COLS*(ROWS-1));
memmove(colorbuf+COLS, colorbuf, COLS*(ROWS-1));
--origin_y;
@ -97,23 +168,31 @@ void scroll_vert(sbyte delta_y) {
}
}
void scroll_setup(void) {
scroll_fine_x = scroll_fine_y = 0;
origin_x = origin_y = 0x80;
swap_needed = true;
void scroll_xy(sbyte delta_x, sbyte delta_y) {
if (delta_x) scroll_horiz(delta_x);
if (delta_y) scroll_vert(delta_y);
}
// get screen buffer addresses
void scroll_setup() {
scroll_fine_x = scroll_fine_y = 0;
origin_x = origin_y = 0;
swap_needed = true;
copy_needed = true;
// setup screen buffer addresses
hidbuf = (byte*) 0x8000;
visbuf = (byte*) 0x8400;
// copy existing text to screen 0
// copy existing screen contents to hidden buffer
memcpy(hidbuf, (byte*)0x400, COLS*ROWS);
// copy screen 1 to screen 0
// copy also to hidden buffer
memcpy(visbuf, hidbuf, COLS*ROWS);
// set VIC bank ($4000-$7FFF)
// set VIC bank ($8000-$BFFF)
// https://www.c64-wiki.com/wiki/VIC_bank
CIA2.pra = 0x01;
SET_VIC_BANK(0x8000);
// set up 24 line / 38 column mode to hide edges
VIC.ctrl1 &= ~0x08; // 24 lines
VIC.ctrl2 &= ~0x08; // 38 columns
}

View File

@ -20,14 +20,32 @@ extern byte swap_needed; // TRUE if scroll_update() swaps
void scroll_setup(void);
// scroll in X or Y directions
void scroll_xy(sbyte delta_x, sbyte delta_y);
void scroll_horiz(sbyte delta_x);
void scroll_vert(sbyte delta_y);
// call this after vblank
// call this right after vblank
void scroll_update(void);
// caller must implement these two
void scroll_draw_column(byte col);
void scroll_draw_row(byte row);
/* incremental scrolling library */
extern int pixofs_x; // X scroll pixel offset
extern int pixofs_y; // Y scroll pixel offset
extern sbyte fine_correct_x;
extern sbyte fine_correct_y;
void scroll_start(byte dir);
void scroll_finish(void);
void scroll_refresh(void);
#define SCROLL_LEFT 1
#define SCROLL_RIGHT 2
#define SCROLL_UP 4
#define SCROLL_DOWN 8
#endif

File diff suppressed because one or more lines are too long

93
presets/c64/siddemo.c Normal file
View File

@ -0,0 +1,93 @@
#include "common.h"
//#link "common.c"
#include <tgi.h>
//#resource "c64-sid.cfg"
#define CFGFILE c64-sid.cfg
//#resource "sidmusic1.bin"
//#link "sidplaysfx.ca65"
#include "sidplaysfx.h"
static const unsigned char Palette[2] =
{ TGI_COLOR_BLUE, TGI_COLOR_WHITE };
static int sweep = 0;
#define BITMAP_START 0xe020
// recursive macros to quickly set bitmap memory
#define SID_SIGNAL_4(index) \
POKE(BITMAP_START + (index) + 0, SID.noise); \
POKE(BITMAP_START + (index) + 1, SID.noise); \
POKE(BITMAP_START + (index) + 2, SID.noise); \
POKE(BITMAP_START + (index) + 3, SID.noise);
#define SID_SIGNAL_16(index) \
SID_SIGNAL_4(index); \
SID_SIGNAL_4(index+4); \
SID_SIGNAL_4(index+8); \
SID_SIGNAL_4(index+12);
#define SID_SIGNAL_64(index) \
SID_SIGNAL_16(index); \
SID_SIGNAL_16(index+16); \
SID_SIGNAL_16(index+32); \
SID_SIGNAL_16(index+48);
void show_signal() {
// push SID voice 3 signal to screen memory
// as fast as we can
SID_SIGNAL_64(0);
SID_SIGNAL_64(64);
SID_SIGNAL_64(128);
SID_SIGNAL_64(192);
}
void show_envelope() {
// read envelope value and plot vertical lines
// via TGI library
byte value = 199 - (SID.read3 >> 1);
tgi_setcolor(0);
tgi_line(sweep, 199 - 128, sweep, value - 1);
tgi_setcolor(1);
tgi_line(sweep, value, sweep, 240);
if (++sweep == 320) sweep = 0;
}
void main(void) {
// install TGI graphics driver
tgi_install(tgi_static_stddrv);
tgi_init();
tgi_clear();
tgi_setpalette(Palette);
// initialize SID player
sid_init(0);
sid_start();
// install joystick driver
joy_install(joy_static_stddrv);
while (1) {
// play sound effect when joystick is moved
byte joy = joy_read(0);
if (joy) {
sid_sfx(joy & 3);
// exit when fire button pressed
if (JOY_BTN_1(joy)) { break; }
}
// sync with frame rate
waitvsync();
// update SID player
sid_update();
// update graphs
show_envelope();
show_signal();
}
tgi_uninstall();
sid_stop();
}

341
presets/c64/side_scroller.c Normal file
View File

@ -0,0 +1,341 @@
#include "common.h"
//#link "common.c"
#include "sprites.h"
//#link "sprites.c"
#include "rasterirq.h"
//#link "rasterirq.ca65"
#include "bcd.h"
//#link "bcd.c"
#include "sidmacros.h"
///// SPRITE DATA
#define NUM_SPRITE_PATTERNS 4
/*{w:12,h:21,bpp:2,brev:1,wpimg:64,count:4,aspect:2}*/
const char SPRITE_DATA[64*NUM_SPRITE_PATTERNS] = {
0x0A,0xAA,0x80,0x0A,0xAA,0x80,0x2A,0xAA,
0xA0,0x2A,0xAA,0xA0,0xAA,0xAA,0xA8,0xFF,
0xD5,0x40,0xCD,0xD7,0x40,0x3D,0xD5,0x54,
0x37,0x55,0x54,0x37,0x54,0x50,0x05,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,0x2A,0xAA,0xAA,0x01,
0x57,0xFF,0x01,0xD7,0x73,0x15,0x57,0x7C,
0x15,0x55,0xDC,0x05,0x15,0xDC,0x00,0x55,
0x50,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,
0x03,0xFF,0xF0,0x0E,0xAA,0xAC,0x0E,0xAA,0xAC,
0x3A,0xAE,0xAB,0x3A,0xBB,0xAB,0x3A,0xBA,0xAB,
0x3A,0xBB,0xAB,0x3A,0xAE,0xAB,0x3A,0xAA,0xAB,
0x35,0xAA,0x97,0x04,0x6A,0x44,0x04,0x15,0x04,
0x01,0x04,0x10,0x01,0x04,0x10,0x01,0x04,0x10,
0x00,0x44,0x40,0x00,0x44,0x40,0x00,0xEA,0xC0,
0x00,0xD9,0xC0,0x00,0xE6,0xC0,0x00,0x3F,0x00,
0x00,
0x00,0x00,0x00,0x00,0xFF,0x00,0x03,0xAA,
0xC0,0x0E,0xAA,0xB0,0x3A,0xAA,0xAC,0xE9,
0x69,0x6B,0xEA,0x69,0xAB,0xEA,0xAA,0xAB,
0xEA,0xAA,0xAB,0x3A,0x96,0xAC,0x3A,0x69,
0xAC,0x0E,0xAA,0xB0,0x0E,0xAA,0xB0,0x0F,
0xAA,0xF0,0x3A,0xAA,0xAC,0xEE,0xAA,0xBB,
0xEE,0xAA,0xBB,0x33,0xAA,0xCC,0x03,0xAB,
0x00,0x00,0xEB,0x00,0x00,0x3F,0xC0,0x00,
};
///// DEFINES
#define GAME_BASE 0x400
#define SCORE_BASE 0x2c00
#define SPRITE_SHAPE 192 // first sprite shape #
#define PLAYER_SHAPE SPRITE_SHAPE
#define POWERUP_SHAPE (SPRITE_SHAPE+2)
#define OBSTACLE_SHAPE (SPRITE_SHAPE+3)
#define PLAYER_INDEX 0 // sprite indices
#define POWERUP_INDEX 1
#define OBSTACLE_INDEX 2
#define CENTER_X 172 // sprite start X coord.
#define FLOOR_Y (128 << 8) // sprite start Y (fixed 8.8)
#define JUMP_VELOCITY (-900) // jump velocity (fixed 8.8)
#define GRAVITY 32 // gravity (fixed 8.8)
#define POWERUP_Y 80 // sprite Y of power up
#define OBSTACLE_Y 96 // sprite Y of obstacle
#define SCROLL_TOP 8 // scroll top row
#define SCROLL_ROWS 14 // scroll # of rows
#define GROUND_ROW 7 // ground row (+ top row)
///// VARIABLES
word player_x; // player X
word player_y; // player Y (fixed 8.8)
signed int player_vel_y; // player Y velocity (fixed 8.8)
byte faceleft = 0; // 0 = face right, 1 = face left
word scroll_x = 0; // current scroll X position
word score = 0; // current player score
///// FUNCTIONS
// display list used by rasterirq.h
// draws scoreboard and sets scroll register
void display_list() {
// set x scroll register to scroll value
SET_SCROLL_X(scroll_x);
// set background color
VIC.bgcolor[0] = COLOR_CYAN;
// next interrupt is two rows from bottom
DLIST_NEXT(248-16);
// set background color
VIC.bgcolor[0] = COLOR_BLUE;
// screen memory = 0x2800
SET_VIC_SCREEN(SCORE_BASE);
// clear x scroll register
SET_SCROLL_X(0);
// next interrupt is bottom of frame
DLIST_NEXT(250);
// reset screen to 0x400
SET_VIC_SCREEN(0x400);
// next interrupt is above top of next frame
DLIST_RESTART(40);
}
void update_scoreboard() {
draw_bcd_word(SCRNADR(SCORE_BASE,7,24), score);
}
void add_score(int delta) {
score = bcd_add(score, delta);
}
// clear scoreboard and draw initial strings
void init_scoreboard() {
memset((void*)SCORE_BASE, ' ', 1024);
memcpy((void*)SCRNADR(SCORE_BASE,1,24), "SCORE:", 6);
update_scoreboard();
}
void init_sprite_shapes() {
sprite_set_shapes(SPRITE_DATA,
SPRITE_SHAPE,
NUM_SPRITE_PATTERNS);
}
void init_sprite_positions() {
// setup sprite positions
player_x = CENTER_X;
player_y = FLOOR_Y;
player_vel_y = 0;
sprshad.spr_color[POWERUP_INDEX] = COLOR_YELLOW;
sprshad.spr_color[OBSTACLE_INDEX] = COLOR_GRAY3;
}
void move_player(byte joy) {
// move sprite based on joystick
if (JOY_LEFT(joy)) {
player_x -= 2;
faceleft = 1;
}
if (JOY_RIGHT(joy)) {
player_x += 1;
faceleft = 0;
}
if (JOY_BTN_1(joy) && player_y == FLOOR_Y) {
player_vel_y = JUMP_VELOCITY;
}
// apply velocity
player_y += player_vel_y;
// apply gravity
player_vel_y += GRAVITY;
// stop velocity when falling thru floor
if (player_y >= FLOOR_Y) {
player_y = FLOOR_Y; // reset Y position
player_vel_y = 0; // reset velocity
}
// keep player from moving offscreen
if (player_x < 36) player_x = 36;
if (player_x > 300) player_x = 300;
// draw player into sprite shadow buffer
sprite_draw(PLAYER_INDEX,
player_x,
player_y >> 8, // fixed point conversion
PLAYER_SHAPE + faceleft);
}
void move_items() {
// move in sync with scrolling world
// draw powerup
sprite_draw(POWERUP_INDEX,
((scroll_x*2) & 0x1ff),
POWERUP_Y,
POWERUP_SHAPE);
// draw obstacle
sprite_draw(OBSTACLE_INDEX,
((scroll_x+256) & 0x1ff),
OBSTACLE_Y,
OBSTACLE_SHAPE);
}
byte get_char_for_row(byte row) {
// ground?
if (row >= GROUND_ROW) { return 253; }
// obstacle?
if (row >= GROUND_ROW-3) {
// only show obstacle for certain values of scroll_x
if ((scroll_x & 0b1110000) == 0) { return 247; }
}
// default is the sky (empty space)
return 32;
}
void draw_right_column() {
// get the top-right corner address of scroll area
word addr = SCRNADR(GAME_BASE, 39, SCROLL_TOP);
byte row;
// draw one character per row
for (row=0; row<SCROLL_ROWS; row++) {
POKE(addr, get_char_for_row(row));
addr += 40;
}
}
void scroll_one_column_left() {
// copy several rows of screen memory
// backwards one byte
const word start = SCRNADR(GAME_BASE, 0, SCROLL_TOP);
const word nbytes = SCROLL_ROWS*40-1;
memcpy((byte*)start, (byte*)start+1, nbytes);
// draw the right column of characters
draw_right_column();
}
void scroll_one_pixel_left() {
// scroll left one pixel
scroll_x -= 1;
// set scroll register with lower three bits
VIC.ctrl2 = (VIC.ctrl2 & ~7) | (scroll_x & 7);
// move screen memory if the scroll register
// has just gone past 0 and wrapped to 7
if ((scroll_x & 7) == 7) {
scroll_one_column_left();
}
}
void detect_player_collision(byte bgcoll, byte sprcoll) {
// did we hit a powerup? (#0 and #1)
bool hit_powerup = (sprcoll & 0b011) == 0b011;
// did player and obstacle sprite (#0 and #2) collide?
bool hit_obstacle = (sprcoll & 0b101) == 0b101;
// did player (#0) collide with background?
hit_obstacle |= (bgcoll & 0b001) != 0;
// did we hit anything bad?
if (hit_obstacle) {
// make player fall downward and backward
player_vel_y = -JUMP_VELOCITY;
player_x -= 1;
sprshad.spr_color[PLAYER_INDEX] = COLOR_LIGHTRED;
SID_PLAY_TONE(500);
if (score != 0) { add_score(0x9999); } // BCD -1
update_scoreboard();
} else {
sprshad.spr_color[PLAYER_INDEX] = COLOR_GREEN;
}
// did we hit powerup?
if (hit_powerup) {
sprshad.spr_color[POWERUP_INDEX] += 1; // cycle colors
SID_PLAY_TONE(8000);
add_score(1);
update_scoreboard();
}
}
void main() {
// clear screen, set background color
clrscr();
VIC.bgcolor[0] = COLOR_CYAN;
VIC.bordercolor = COLOR_BLUE;
// set vertical scroll = 3, 25 rows
VIC.ctrl1 = 0b00011011;
// set 38 column mode (for X scrolling)
VIC.ctrl2 = 0b00000000;
// set uniform color of characters
memset(COLOR_RAM, COLOR_WHITE, 1000);
// install the joystick driver
joy_install (joy_static_stddrv);
// setup sound
SID_INIT(8,0);
// set multicolor sprites and colors in shadow buffer
sprite_clear(); // clear shadow buffer
sprshad.spr_mcolor = 0b11111111; // all sprites multicolor
sprshad.spr_exp_y = 1; // double height
// set colors
sprshad.spr_color[PLAYER_INDEX] = COLOR_GREEN;
VIC.spr_mcolor0 = COLOR_GRAY2;
VIC.spr_mcolor1 = COLOR_BLACK;
// setup sprites
init_sprite_shapes();
init_sprite_positions();
// setup scoreboard
init_scoreboard();
// setup rasterirq library for scoreboard split
DLIST_SETUP(display_list);
// game loop, repeat forever
while (1) {
// saved collision flags
byte sprcoll, bgcoll;
// wait for end of frame
waitvsync();
//--- START TIME CRITICAL SECTION
// grab and reset collision flags
sprcoll = VIC.spr_coll;
bgcoll = VIC.spr_bg_coll;
// update sprite registers from sprite shadow buffer
sprite_update(DEFAULT_SCREEN);
// scroll screen
scroll_one_pixel_left();
//--- END TIME CRITICAL SECTION
// use collision flags to see if player collided
detect_player_collision(bgcoll, sprcoll);
// get joystick bits and move player
move_player(joy_read(0));
// move obstacle and powerup sprites
move_items();
}
}

55
presets/c64/sidmacros.h Normal file
View File

@ -0,0 +1,55 @@
// SID voices are v1, v2, v3
// wave options flags
#define SID_GATE 0x01
#define SID_SYNC 0x02
#define SID_RINGMOD 0x04
#define SID_TESTBIT 0x08
#define SID_TRIANGLE 0x10
#define SID_SAWTOOTH 0x20
#define SID_SQUARE 0x40
#define SID_NOISE 0x80
// Init SID global volume
// volume: 0-15
// filters: bitmask
#define SID_INIT(volume, filters) \
SID.amp = (volume) | ((filters)<<4);
// stop voice
#define SID_STOP(voice) \
SID.voice.ctrl &= ~SID_GATE;
// start voice
#define SID_START(voice) \
SID.voice.ctrl |= SID_GATE;
// set ADSR envelope
#define SID_ADSR(voice,attack,decay,sustain,release) \
SID.voice.ad = ((decay)&15) | ((attack)<<4); \
SID.voice.sr = ((release)&15) | ((sustain)<<4);
// set frequency (0 - 65535)
#define SID_FREQ(voice,_freq) \
SID.voice.freq = (_freq);
// set pulse width (0 - 4095)
#define SID_PULSEWIDTH(voice,_pw) \
SID.voice.pw = (_pw);
// set wave shape and options
#define SID_WAVE(voice,options) \
SID.voice.ctrl = (SID.voice.ctrl & 1) | (options)
// play a quick square wave pulse
#define SID_PULSE_DECAY(voice, freq) \
SID_STOP(voice) \
SID_FREQ(voice,freq); \
SID_PULSEWIDTH(voice,0x200); \
SID_ADSR(voice,8,8,0,4); \
SID_WAVE(voice,SID_SQUARE|SID_GATE); \
// play a tone if one is not already playing
#define SID_PLAY_TONE(freq) \
if (!SID.read3) { SID_PULSE_DECAY(v3, (freq)) }

BIN
presets/c64/sidmusic1.bin Normal file

Binary file not shown.

View File

@ -0,0 +1,72 @@
; music and SFX from GoatTracker 2 sample files
; http://sourceforge.net/projects/goattracker2
.segment "DATA"
_sid_playing: .byte $00
.segment "SIDFILE"
.incbin "sidmusic1.bin"
.segment "LOWCODE"
.global _sid_init, _sid_update, _sid_sfx
.global _sid_start, _sid_stop, _sid_playing
_sid_init:
jmp $1000
_sid_update:
bit _sid_playing
bpl @noplay
jmp $1003
@noplay:
rts
_sid_sfx:
tax
lda sfxtbllo,x ;Address in A,Y
ldy sfxtblhi,x
ldx #$0e ;Channel index in X
jmp $1006 ;(0, 7 or 14)
_sid_start:
lda #$80
bne skipstop
_sid_stop:
lda #$00
sta $d418
skipstop:
sta _sid_playing
rts
sfxtbllo: .byte <arpeggio2
.byte <arpeggio1
.byte <gunshot
.byte <explosion
sfxtblhi: .byte >arpeggio2
.byte >arpeggio1
.byte >gunshot
.byte >explosion
arpeggio2:
.byte $00,$89,$04,$A2,$41,$A2,$A2,$A6,$A6,$A6,$40,$A9,$A9,$A9,$A2,$A2
.byte $A2,$A6,$A6,$A6,$A9,$A9,$A9,$A2,$A2,$A2,$A6,$A6,$A6,$A9,$A9,$A9
.byte $A2,$A2,$A2,$A6,$A6,$A6,$A9,$A9,$A9,$00
arpeggio1:
.byte $0A,$00,$02,$A0,$41,$A0,$A0,$A4,$A4,$A4,$A7,$A7,$A7,$A0,$A0,$A0
.byte $A4,$A4,$A4,$A7,$A7,$A7,$A0,$A0,$A0,$A4,$A4,$A4,$A7,$A7,$A7,$A0
.byte $A0,$A0,$A4,$A4,$A4,$A7,$A7,$A7,$00
gunshot:
.byte $00,$F9,$08,$C4,$81,$A8,$41,$C0,$81,$BE,$BC,$80,$BA,$B8,$B6,$B4
.byte $B2,$B0,$AE,$AC,$AA,$A8,$A6,$A4,$A2,$A0,$9E,$9C,$9A,$98,$96,$94
.byte $92,$90,$00
explosion:
.byte $00,$FA,$08,$B8,$81,$A4,$41,$A0,$B4,$81,$98,$92,$9C,$90,$95,$9E
.byte $92,$80,$94,$8F,$8E,$8D,$8C,$8B,$8A,$89,$88,$87,$86,$84,$00

7
presets/c64/sidplaysfx.h Normal file
View File

@ -0,0 +1,7 @@
extern void sid_init(char musicindex);
extern void sid_update(void);
extern void sid_sfx(char sfxindex);
extern void sid_start(void);
extern void sid_stop(void);

View File

@ -3,33 +3,20 @@ Text-based version of a Blockade-style game.
For more information, see "Making Arcade Games in C".
*/
#include <stdlib.h>
#include <string.h>
#include <joystick.h>
#include <conio.h>
#include <c64.h>
#include <cbm_petscii_charmap.h>
#include <peekpoke.h>
#define COLS 40
#define ROWS 24
typedef unsigned char byte;
typedef signed char sbyte;
typedef unsigned short word;
#include "common.h"
// BASL = text address of cursor position
static byte** BASL = (byte**) 0xD1;
byte getchar(byte x, byte y) {
// get the character at a specfic x/y position
byte readcharxy(byte x, byte y) {
gotoxy(x,y); // set cursor position
return (*BASL)[x]; // lookup value @ cursor address
}
void delay(byte count) {
while (count--) {
word i;
for (i=0; i<200; i++) ;
waitvsync();
}
}
@ -115,7 +102,7 @@ void move_player(Player* p) {
cputcxy(p->x, p->y, p->tail_attr);
p->x += DIR_X[p->dir];
p->y += DIR_Y[p->dir];
if ((getchar(p->x, p->y) & 0x7f) != ' ')
if ((readcharxy(p->x, p->y) & 0x7f) != ' ')
p->collided = 1;
draw_player(p);
}
@ -141,7 +128,8 @@ byte ai_try_dir(Player* p, dir_t dir, byte shift) {
dir &= 3;
x = p->x + (DIR_X[dir] << shift);
y = p->y + (DIR_Y[dir] << shift);
if (x < COLS && y < ROWS && (getchar(x, y) & 0x7f) == ' ') {
if (x < COLS && y < ROWS
&& (readcharxy(x, y) & 0x7f) == ' ') {
p->dir = dir;
return 1;
} else {

View File

@ -1,10 +1,17 @@
#include <stdio.h>
#include <conio.h>
#include <stdio.h>
#include <stdlib.h>
#include <peekpoke.h>
#include <string.h>
#include <c64.h>
#include <cbm_petscii_charmap.h>
#include "common.h"
//#link "common.c"
void main(void) {
clrscr();
printf("\r\nHello World!\r\n");
printf("\nHello World!\n");
getchar();
}

View File

@ -0,0 +1,121 @@
#include "common.h"
//#link "common.c"
#include "sprites.h"
//#link "sprites.c"
/*{w:12,h:21,bpp:2,brev:1}*/
const char SPRITEMC[3*21] = {
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
};
#define SPRITE_SHAPE 192
// X/Y position arrays
int xpos[8]; // fixed point 9.7
int ypos[8]; // fixed point 8.8
// X/Y velocity arrays
int xvel[8];
int yvel[8];
void init_sprites(void) {
byte i;
// setup sprite positions
for (i=0; i<8; i++) {
xpos[i] = ((i & 3) * 0x2000) - 0x3000;
ypos[i] = (i * 0x1000) - 0x3000;
sprshad.spr_color[i] = i | 8;
}
}
void move_sprites(void) {
byte i;
for (i=0; i<8; i++) {
//VIC.bordercolor = i;
sprite_draw(i,
(xpos[i] >> 7) + 172,
(ypos[i] >> 8) + 145,
SPRITE_SHAPE);
// update position
xpos[i] += xvel[i];
ypos[i] += yvel[i];
}
}
void update_sprites(void) {
byte i;
for (i=0; i<8; i++) {
// update velocity
xvel[i] -= xpos[i] >> 12;
yvel[i] -= ypos[i] >> 12;
}
}
void collide_sprites(byte spr_coll) {
byte i;
byte mask = 1;
// exit if no collisions
if (!spr_coll) return;
// iterate all sprites that have their flag set
for (i=0; i<8; i++, mask<<=1) {
//VIC.bordercolor = i;
if (spr_coll & mask) {
// find the first sprite that intersects
byte j = sprite_get_closest_collision(i, spr_coll);
// returns 0..7 if a sprite was found
if (j < 8) {
xvel[i] = (xpos[i] - xpos[j]) >> 4;
yvel[i] = (ypos[i] - ypos[j]) >> 4;
}
}
}
}
void iterate_game(void) {
byte spr_coll;
// wait for vblank
waitvsync();
// grab and reset sprite-sprite collision flags
spr_coll = VIC.spr_coll;
// then update sprite registers from shadow RAM
sprite_update(DEFAULT_SCREEN);
// draw sprites into shadow ram
move_sprites();
// and update velocity and position
update_sprites();
// if any flags are set in the collision register,
// process sprite collisions
collide_sprites(spr_coll);
}
void main(void) {
clrscr();
VIC.bordercolor = 0;
// setup sprite library and copy sprite to VIC bank
sprite_clear();
sprite_set_shapes(SPRITEMC, SPRITE_SHAPE, 1);
// set colors
sprshad.spr_mcolor = 0xff;
VIC.spr_mcolor0 = 4;
VIC.spr_mcolor1 = 7;
// set sprite initial positions
init_sprites();
// game loop
while (1) {
iterate_game();
}
}

View File

@ -4,26 +4,33 @@
SpriteShadow sprshad;
void sprite_update(char* vicbank) {
memcpy(vicbank + 0x3f8, sprshad.spr_shapes, 8);
VIC.spr_ena = sprshad.spr_ena;
VIC.spr_hi_x = sprshad.spr_hi_x;
void sprite_clear(void) {
memset(&sprshad, 0, sizeof(sprshad));
}
void sprite_update(byte* screenmem) {
memcpy(screenmem + 0x3f8, sprshad.spr_shapes, 8);
memcpy(VIC.spr_pos, sprshad.spr_pos, 16);
memcpy(VIC.spr_color, sprshad.spr_color, 8);
VIC.spr_ena = sprshad.spr_ena;
VIC.spr_hi_x = sprshad.spr_hi_x;
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_set_shapes(const void* sprite_data,
byte index,
byte count)
{
memcpy(get_vic_bank_start() + index * 64,
sprite_data,
64 * count);
}
const byte BITS[8] = {
0x01, 0x02, 0x04, 0x08, 0x10, 0x20, 0x40, 0x80,
0x01, 0x02, 0x04, 0x08, 0x10, 0x20, 0x40, 0x80
};
void sprite_draw(byte i, word x, byte y, byte shape) {
@ -38,7 +45,25 @@ void sprite_draw(byte i, word x, byte y, byte shape) {
sprshad.spr_shapes[i] = shape;
}
void sprite_clear(void) {
sprshad.spr_ena = 0;
byte sprite_get_closest_collision(byte i, byte spr_coll) {
byte j;
byte jmask = 1;
byte dx,dy;
if (spr_coll & BITS[i]) {
spr_coll ^= BITS[i];
for (j=0; j<8; j++, jmask<<=1) {
if (spr_coll & jmask) {
// TODO?
dx = sprshad.spr_pos[i].x - sprshad.spr_pos[j].x + 24;
if (dx < 48) {
dy = sprshad.spr_pos[i].y - sprshad.spr_pos[j].y + 21;
if (dy < 42) {
return j;
}
}
}
}
} else {
return 0xff;
}
}

View File

@ -3,28 +3,40 @@
#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 */
typedef struct SpriteShadow {
struct {
byte x; /* X coordinate */
byte y; /* Y coordinate */
} spr_pos[8];
byte spr_hi_x; /* High bits of X coordinate */
byte spr_ena; /* Enable sprites */
byte spr_exp_y; /* Expand sprites in Y dir */
byte spr_bg_prio; /* Priority to background */
byte spr_mcolor; /* Sprite multicolor bits */
byte spr_exp_x; /* Expand sprites in X dir */
byte spr_color[8]; /* Colors for the sprites */
byte spr_shapes[8]; /* sprite shapes */
} SpriteShadow;
/* sprite shadow buffer */
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);
/* set one or more sprite patterns from a byte array */
void sprite_set_shapes(const void* sprite_data,
byte index,
byte count);
/* clear all sprites from shadow buffer */
void sprite_clear();
/* draw a sprite into shadow buffer */
void sprite_draw(byte index, word x, byte y, byte shape);
/* update sprite registers */
void sprite_update(byte* screenmem);
/* get the closest sprite collision
given the set of collision flags */
byte sprite_get_closest_collision(byte index, byte spr_coll);
// bit lookup table
extern const byte BITS[8];
#endif

View File

@ -0,0 +1,75 @@
//#link "common.c"
#include "common.h"
//#link "rasterirq.ca65"
#include "rasterirq.h"
//#link "sprites.c"
#include "sprites.h"
#include <cbm_petscii_charmap.h>
/*{w:24,h:21,bpp:1,brev:1}*/
const char spriteshape[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
};
byte scroll_x = 0;
void dlist_example(void) {
VIC.bordercolor = 6;
VIC.bordercolor = 5;
VIC.ctrl1 = 0x18;
VIC.ctrl2 = VIC.ctrl2 & 0xf8;
VIC.ctrl2 |= (scroll_x & 7);
DLIST_NEXT(150);
// VIC.ctrl1 = 5 | 0x18;
VIC.bordercolor = 2;
sprshad.spr_pos[0].y += 1;
scroll_x++;
VIC.addr ^= 0xf0;
VIC.ctrl2 = VIC.ctrl2 & 0xf8;
DLIST_NEXT(0xf9);
VIC.ctrl1 = 0x10;
VIC.bordercolor = 3;
VIC.addr ^= 0xf0;
DLIST_NEXT(0xfc);
VIC.ctrl1 = 0x18;
VIC.bordercolor = 4;
DLIST_RESTART(30);
}
void main(void) {
byte i;
clrscr();
sprite_clear();
sprite_set_shapes(spriteshape, 192, 1);
// set colors
sprshad.spr_exp_x = 0xff;
for (i=0; i<8; i++) {
sprshad.spr_color[i] = i+3;
sprite_draw(i, i*38+24, 248, 192);
}
// TODO: can't do in IRQ
DLIST_SETUP(dlist_example);
while (1) {
waitvsync();
sprite_update(DEFAULT_SCREEN);
printf("Raster IRQ-driven display list! ");
}
}

View File

@ -0,0 +1,136 @@
#include "common.h"
//#link "common.c"
#include "sprites.h"
//#link "sprites.c"
#define NUM_SPRITE_PATTERNS 2
#define SPRITE_SHAPE 192
#define SPRITES_PER_ROW 6
/*{w:12,h:21,bpp:2,brev:1,count:2}*/
const char SPRITEMC[64*NUM_SPRITE_PATTERNS] = {
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,
0,
0x00,0xAE,0x80,0x02,0xBF,0xA0,0x0B,0xBF,0xB8,
0x0B,0xBF,0xB8,0x0B,0xBF,0xB8,0x0B,0xBF,0xB8,
0x0B,0xBF,0xB8,0x0B,0xBF,0xB8,0x0B,0xAE,0xB8,
0x09,0xAE,0x98,0x08,0x6E,0x48,0x08,0x1D,0x08,
0x02,0x0C,0x20,0x02,0x0C,0x20,0x02,0x0C,0x20,
0x08,0x8C,0x88,0x08,0x8C,0x88,0x20,0x55,0x42,
0x20,0x77,0x42,0x20,0x5D,0x42,0x20,0x15,0x02,
0,
};
SpriteShadow* sprite_rows[3]; // row sprite buffers
int player_x = 172; // player X coordinate
byte player_y = 222; // player Y coordinate
void init_sprites(void) {
byte row,i;
// iterate through each row of sprites
for (row=0; row<3; row++) {
// fill in data for local sprite buffer
sprite_clear();
sprshad.spr_mcolor = 0xff;
for (i=0; i<SPRITES_PER_ROW; i++) {
sprite_draw(i, i*50+50, row*50+60, SPRITE_SHAPE);
sprshad.spr_color[i] = (i+row)|8;
}
// allocate sprite buffer for row
sprite_rows[row] = (SpriteShadow*) malloc(sizeof(SpriteShadow));
// and copy local buffer into it
*sprite_rows[row] = sprshad;
}
}
void draw_sprite_row(byte row, byte rasterline) {
// copy sprite row data to sprite shadow buffer
sprshad = *sprite_rows[row];
// player is part of row 2, draw player?
if (row == 2) {
sprite_draw(7, player_x, player_y, SPRITE_SHAPE+1);
sprshad.spr_color[7] = 15;
}
// wait for the raster line
raster_wait(rasterline);
// then update sprite registers from shadow buffer
VIC.bordercolor = row+1; // (so we see the timing)
sprite_update(DEFAULT_SCREEN);
VIC.bordercolor = 0;
}
void move_sprite_x(SpriteShadow* spr,
byte index,
sbyte delta_x)
{
word x = spr->spr_pos[index].x;
byte mask = BITS[index]; // lookup table for (1 << index)
if (spr->spr_hi_x & mask) {
x |= 0x100;
}
x += delta_x;
spr->spr_pos[index].x = x;
if (x & 0x100) {
spr->spr_hi_x |= mask;
} else {
spr->spr_hi_x &= ~mask;
}
}
void move_sprites() {
byte i;
for (i=0; i<SPRITES_PER_ROW; i++) {
move_sprite_x(sprite_rows[0], i, 3);
move_sprite_x(sprite_rows[1], i, -2);
}
}
void move_player() {
byte joy = PEEK(0xdc01); // read joystick #0
if (joy & 0x8) { player_x -= 1; } // left
if (joy & 0x4) { player_x += 1; } // right
}
void iterate_game(void) {
waitvsync();
draw_sprite_row(0, 1);
draw_sprite_row(1, 60+21);
draw_sprite_row(2, 110+21);
move_sprites();
move_player();
VIC.bordercolor = 9;
}
void main(void) {
VIC.bordercolor = 0;
clrscr();
// setup sprite library and copy sprite to VIC bank
sprite_clear();
sprite_set_shapes(SPRITEMC, SPRITE_SHAPE, NUM_SPRITE_PATTERNS);
// set colors
VIC.spr_mcolor0 = 4;
VIC.spr_mcolor1 = 7;
// set sprite initial positions
init_sprites();
// turn off interrupts so we don't mess up timing
asm("sei");
// game loop
while (1) {
iterate_game();
}
}

View File

@ -0,0 +1,135 @@
#include "common.h"
//#link "common.c"
#include "multisprite.h"
//#link "multisprite.ca65"
#include "rasterirq.h"
//#link "rasterirq.ca65"
#define NUM_TEST_SPRITES 24
byte* sprite_bank = (byte*)DEFAULT_SCREEN + 0x3f8;
/*{w:12,h:21,bpp:2,brev:1}*/
const char SPRITEMC[3*21] = {
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
};
void sprite_shape(char* vicbank, byte index, const char* sprite_data) {
memmove(vicbank + index*64, sprite_data, 64);
}
void setup_sprites() {
byte i;
sprite_shape((void*)0x0, 255, SPRITEMC);
for (i=0; i<MAX_MSPRITES; i++) {
msprite_order[i] = i;
msprite_y[i] = 255;
}
for (i=0; i<NUM_TEST_SPRITES; i++) {
int x = i*13+20;
msprite_x_lo[i] = x;
msprite_x_hi[i] = x>>8;
msprite_y[i] = i*0+50;
// msprite_flags[i] = 0;
msprite_shape[i] = 255;
msprite_color[i] = i|8;
}
}
// constants for display list
#define Y0 21
#define Y1 35
#define YS 42
void display_list() {
msprite_render_init();
msprite_render_section();
DLIST_NEXT(Y1+YS*1);
msprite_render_section();
DLIST_NEXT(Y1+YS*2);
msprite_render_section();
DLIST_NEXT(Y1+YS*3);
msprite_render_section();
DLIST_NEXT(Y1+YS*4);
msprite_render_section();
VIC.bordercolor = 3;
msprite_sort();
VIC.bordercolor = 4;
msprite_add_velocity(NUM_TEST_SPRITES);
VIC.bordercolor = 0;
DLIST_RESTART(Y0);
}
void msprite_set_position(byte index, int x, byte y) {
asm("sei");
msprite_x_lo[index] = x;
msprite_x_hi[index] = x >> 8;
msprite_y[index] = y;
asm("cli");
}
void msprite_add_position(byte index, byte dx, byte dy) {
int x;
x = msprite_x_lo[index] | msprite_x_hi[index]*256;
x += dx;
asm("sei");
msprite_x_lo[index] = x;
msprite_x_hi[index] = x >> 8;
msprite_y[index] += dy;
asm("cli");
}
void apply_gravity() {
byte i;
for (i=0; i<NUM_TEST_SPRITES; i++) {
int xvel = msprite_xvel_lo[i] + msprite_xvel_hi[i]*256;
int yvel = msprite_yvel_lo[i] + msprite_yvel_hi[i]*256;
int xpos = msprite_x_lo[i] + msprite_x_hi[i]*256;
int ypos = msprite_y[i];
xpos -= 172;
ypos -= 145;
xvel -= xpos / 8;
yvel -= ypos / 8;
msprite_xvel_lo[i] = xvel;
msprite_xvel_hi[i] = xvel >> 8;
msprite_yvel_lo[i] = yvel;
msprite_yvel_hi[i] = yvel >> 8;
}
}
void do_test() {
byte i;
raster_wait(160);
for (i=0; i<NUM_TEST_SPRITES; i++) {
msprite_yvel_lo[i] = i*8;
//msprite_add_position(i,i&3,i&3);
}
}
void main() {
clrscr();
setup_sprites();
// set colors
VIC.spr_mcolor = 0xff;
VIC.spr_mcolor0 = 4;
VIC.spr_mcolor1 = 7;
DLIST_SETUP(display_list);
while (1) {
do_test();
}
}

View File

@ -0,0 +1,149 @@
#include "common.h"
//#link "common.c"
//#link "multisprite.ca65"
#include "rasterirq.h"
//#link "rasterirq.ca65"
#define NUM_TEST_SPRITES 24
byte* sprite_bank = (byte*)DEFAULT_SCREEN + 0x3f8;
/*{w:12,h:21,bpp:2,brev:1}*/
const char SPRITEMC[3*21] = {
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
};
/*
const byte BITS[8] = {
0x01, 0x02, 0x04, 0x08, 0x10, 0x20, 0x40, 0x80
};
const byte NOTBITS[8] = {
~0x01, ~0x02, ~0x04, ~0x08, ~0x10, ~0x20, ~0x40, ~0x80
};
typedef struct MSpriteFlags {
byte xhi:1;
byte exp_x:1;
byte exp_y:1;
byte mcolor:1;
byte bgprio:1;
} MSpriteFlags;
*/
#include "multisprite.h"
void sprite_shape(char* vicbank, byte index, const char* sprite_data) {
memmove(vicbank + index*64, sprite_data, 64);
}
void setup_sprites() {
byte i;
sprite_shape((void*)0x0, 255, SPRITEMC);
for (i=0; i<MAX_MSPRITES; i++) {
msprite_order[i] = i;
msprite_y[i] = 255;
}
for (i=0; i<NUM_TEST_SPRITES; i++) {
int x = i*13+20;
msprite_x_lo[i] = x;
msprite_x_hi[i] = x>>8;
msprite_y[i] = i*0+50;
// msprite_flags[i] = 0;
msprite_shape[i] = 255;
msprite_color[i] = i|8;
}
}
void display_list() {
msprite_render_init();
msprite_render_section();
DLIST_NEXT(Y1+YS*1);
msprite_render_section();
DLIST_NEXT(Y1+YS*2);
msprite_render_section();
DLIST_NEXT(Y1+YS*3);
msprite_render_section();
DLIST_NEXT(Y1+YS*4);
msprite_render_section();
DLIST_NEXT(Y1+YS*5);
msprite_render_section();
VIC.bordercolor = 3;
msprite_sort();
VIC.bordercolor = 4;
msprite_add_velocity(NUM_TEST_SPRITES);
VIC.bordercolor = 0;
DLIST_RESTART(Y0);
}
void msprite_set_position(byte index, int x, byte y) {
asm("sei");
msprite_x_lo[index] = x;
msprite_x_hi[index] = x >> 8;
msprite_y[index] = y;
asm("cli");
}
void msprite_add_position(byte index, byte dx, byte dy) {
int x;
x = msprite_x_lo[index] | msprite_x_hi[index]*256;
x += dx;
asm("sei");
msprite_x_lo[index] = x;
msprite_x_hi[index] = x >> 8;
msprite_y[index] += dy;
asm("cli");
}
void apply_gravity() {
byte i;
for (i=0; i<NUM_TEST_SPRITES; i++) {
int xvel = msprite_xvel_lo[i] + msprite_xvel_hi[i]*256;
int yvel = msprite_yvel_lo[i] + msprite_yvel_hi[i]*256;
int xpos = msprite_x_lo[i] + msprite_x_hi[i]*256;
int ypos = msprite_y[i];
xpos -= 172;
ypos -= 145;
xvel -= xpos / 8;
yvel -= ypos / 8;
msprite_xvel_lo[i] = xvel;
msprite_xvel_hi[i] = xvel >> 8;
msprite_yvel_lo[i] = yvel;
msprite_yvel_hi[i] = yvel >> 8;
}
}
void do_test() {
byte i;
raster_wait(160);
for (i=0; i<NUM_TEST_SPRITES; i++) {
msprite_yvel_lo[i] = i*8;
//msprite_add_position(i,i&3,i&3);
}
}
void main() {
clrscr();
setup_sprites();
// set colors
VIC.spr_mcolor = 0xff;
VIC.spr_mcolor0 = 4;
VIC.spr_mcolor1 = 7;
DLIST_SETUP(display_list);
while (1) {
do_test();
}
}

View File

@ -1,11 +1,7 @@
// ported from
// https://odensskjegg.home.blog/2018/12/29/recreating-the-commodore-64-user-guide-code-samples-in-cc65-part-three-sprites/
#include <stdio.h>
#include <stdlib.h>
#include <conio.h>
#include <peekpoke.h>
#include <c64.h>
#include "common.h"
/*{w:24,h:21,bpp:1,brev:1}*/
const char sprite[3*21] = {

View File

@ -0,0 +1,72 @@
processor 6502
include "basicheader.dasm"
Src equ $02
Dest equ $04
Start:
lda #$38 ; 25 rows, on, bitmap
sta $d011 ; VIC control #1
lda #$18 ; 40 column, multicolor
sta $d016 ; VIC control #2
lda #$02
sta $dd00 ; set VIC bank ($4000-$7FFF)
lda #$80
sta $d018 ; set VIC screen to $6000
lda XtraData+0
sta $d020 ; border
sta $d021 ; background
lda #0
sta Dest
; copy char memory
lda #<CharData
sta Src
lda #>CharData
sta Src+1
lda #$40
sta Dest+1
ldx #$20
jsr CopyMem
; copy screen memory
lda #<ScreenData
sta Src
lda #>ScreenData
sta Src+1
lda #$60
sta Dest+1
ldx #$04
jsr CopyMem
; copy color RAM
lda #<ColorData
sta Src
lda #>ColorData
sta Src+1
lda #$d8
sta Dest+1
ldx #4
jsr CopyMem
; infinite loop
jmp .
; copy data from Src to Dest
; X = number of bytes * 256
CopyMem
ldy #0
.Loop
lda (Src),y
sta (Dest),y
iny
bne .Loop
inc Src+1
inc Dest+1
dex
bne .Loop
rts
; bitmap data
CharData equ .
ScreenData equ CharData+8000
ColorData equ ScreenData+1000
XtraData equ ColorData+1000
incbin "badspacerobots-c64.multi.bin"

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

View File

@ -894,21 +894,19 @@ export abstract class BaseMachinePlatform<T extends Machine> extends BaseDebugPl
pause() {
this.timer.stop();
this.audio && this.audio.stop();
// i guess for runToVsync()?
if (this.probeRecorder) {
this.probeRecorder.singleFrame = true;
}
}
// so probe views stick around TODO: must be a better way?
runToVsync() {
if (this.probeRecorder) {
this.probeRecorder.clear();
this.probeRecorder.singleFrame = false;
}
super.runToVsync();
}
// TODO: reset target clock counter
// so probe views stick around TODO: must be a better way?
runToVsync() {
this.restartDebugging();
var flag = false;
this.runEval( () : boolean => {
if (this.getRasterScanline() > 0) flag = true;
else return flag;
});
}
// TODO: reset target clock counter
getRasterScanline() {
return isRaster(this.machine) && this.machine.getRasterY();
}

View File

@ -53,7 +53,9 @@ export enum KeyFlags {
export function _setKeyboardEvents(canvas:HTMLElement, callback:KeyboardCallback) {
canvas.onkeydown = (e) => {
callback(e.which, 0, KeyFlags.KeyDown|_metakeyflags(e));
if (e.ctrlKey || e.which == 8 || e.which == 9 || e.which == 27) { // eat backspace, tab, escape keys
// eat backspace, tab, escape, slash, ' keys
if (e.ctrlKey || e.which == 8 || e.which == 9 || e.which == 27
|| e.which == 191 || e.which == 191 || e.which == 222) {
e.preventDefault();
}
};

View File

@ -52,7 +52,7 @@ export class SourceFile {
}
}
// TODO: smarter about looking for source lines between two addresses
findLineForOffset(PC:number, lookbehind:number) {
findLineForOffset(PC:number, lookbehind:number) : SourceLine {
if (this.offset2loc) {
for (var i=0; i<=lookbehind; i++) {
var loc = this.offset2loc[PC];

View File

@ -27,22 +27,23 @@ export type SelectableTilemap = {
}
export type PixelEditorImageFormat = {
w:number
h:number
count?:number
bpp?:number
np?:number
bpw?:number
sl?:number
pofs?:number
remap?:number[]
reindex?:number[]
brev?:boolean
flip?:boolean
w:number // width
h:number // height
count?:number // # of images
bpp?:number // bits per pixel
np?:number // number of planes
bpw?:number // bits per word
sl?:number // words per line
pofs?:number // plane offset
remap?:number[] // remap array
reindex?:number[] // reindex array
brev?:boolean // bit reverse (msb is leftmost)
flip?:boolean // flip vertically
skip?:number // skip bytes
wpimg?:number // words per image
aspect?:number // aspect ratio
xform?:string // CSS transform
destfmt?:PixelEditorImageFormat
xform?:string
skip?:number
aspect?:number
};
export type PixelEditorPaletteFormat = {
@ -162,12 +163,13 @@ export function convertWordsToImages(words:UintArray, fmt:PixelEditorImageFormat
var mask = (1 << bpp)-1;
var pofs = fmt.pofs || wordsperline*height*count;
var skip = fmt.skip || 0;
var wpimg = fmt.wpimg || wordsperline*height;
var images = [];
for (var n=0; n<count; n++) {
var imgdata = [];
for (var y=0; y<height; y++) {
var yp = fmt.flip ? height-1-y : y;
var ofs0 = n*wordsperline*height + yp*wordsperline;
var ofs0 = wpimg*n + yp*wordsperline;
var shift = 0;
for (var x=0; x<width; x++) {
var color = 0;
@ -202,19 +204,22 @@ export function convertImagesToWords(images:Uint8Array[], fmt:PixelEditorImageFo
var mask = (1 << bpp)-1;
var pofs = fmt.pofs || wordsperline*height*count;
var skip = fmt.skip || 0;
var wpimg = fmt.wpimg || wordsperline*height;
var words;
if (nplanes > 0 && fmt.sl) // TODO?
words = new Uint8Array(wordsperline*height*count);
words = new Uint8Array(wpimg*count);
else if (bitsperword <= 8)
words = new Uint8Array(wordsperline*height*count*nplanes);
words = new Uint8Array(wpimg*count*nplanes);
else
words = new Uint32Array(wordsperline*height*count*nplanes);
words = new Uint32Array(wpimg*count*nplanes);
for (var n=0; n<count; n++) {
var imgdata = images[n];
var i = 0;
for (var y=0; y<height; y++) {
var yp = fmt.flip ? height-1-y : y;
var ofs0 = n*wordsperline*height + yp*wordsperline;
var ofs0 = n*wpimg + yp*wordsperline;
var shift = 0;
for (var x=0; x<width; x++) {
var color = imgdata[i++];
@ -455,7 +460,7 @@ export class TextDataNode extends CodeProjectDataNode {
}
updateLeft() {
if (this.right.words.length != this.words.length)
throw Error("Expected " + this.right.words.length + " bytes; image has " + this.words.length);
throw Error("Cannot put " + this.right.words.length + " image bytes into array of " + this.words.length + " bytes");
this.words = this.right.words;
// TODO: reload editors?
var datastr = this.text.substring(this.start, this.end);
@ -1072,7 +1077,12 @@ class PixEditor extends Viewer {
commit() {
this.updateImage();
this.left.refreshLeft();
try {
this.left.refreshLeft();
} catch (e) {
console.log(e);
alert(`Could not update source code. ${e}`);
}
}
remapPixels(mapfn : (x:number,y:number) => number) {

View File

@ -14,8 +14,8 @@ import { StateRecorderImpl } from "../common/recorder";
import { GHSession, GithubService, getRepos, parseGithubURL } from "./services";
import Split = require('split.js');
import { importPlatform } from "../platform/_index";
import { DisassemblerView, ListingView, SourceEditor } from "./views/editors";
import { AddressHeatMapView, BinaryFileView, MemoryMapView, MemoryView, ProbeLogView, ProbeSymbolView, RasterPCHeatMapView, ScanlineIOView, VRAMMemoryView } from "./views/debugviews";
import { DisassemblerView, ListingView, PC_LINE_LOOKAHEAD, SourceEditor } from "./views/editors";
import { AddressHeatMapView, BinaryFileView, MemoryMapView, MemoryView, ProbeLogView, ProbeSymbolView, RasterPCHeatMapView, RasterStackMapView, ScanlineIOView, VRAMMemoryView } from "./views/debugviews";
import { AssetEditorView } from "./views/asseteditor";
import { isMobileDevice } from "./views/baseviews";
import { CallStackView, DebugBrowserView } from "./views/treeviews";
@ -80,11 +80,16 @@ var store : LocalForage; // persistent store
export var compparams; // received build params from worker
export var lastDebugState : EmuState; // last debug state (object)
type DebugCommandType = null
| 'toline' | 'step' | 'stepout' | 'stepover'
| 'tovsync' | 'stepback' | 'restart';
var lastDebugInfo; // last debug info (CPU text)
var debugCategory; // current debug category
var debugTickPaused = false;
var recorderActive = false;
var lastViewClicked = null;
var lastViewClicked : string = null;
var lastDebugCommand : DebugCommandType = null;
var errorWasRuntime = false;
var lastBreakExpr = "c.PC == 0x6000";
@ -360,7 +365,8 @@ function refreshWindowList() {
});
// TODO: only if raster
addWindowItem("#crtheatmap", "CRT Probe", () => {
return new RasterPCHeatMapView();
//return new RasterPCHeatMapView();
return new RasterStackMapView();
});
addWindowItem("#probelog", "Probe Log", () => {
return new ProbeLogView();
@ -1425,30 +1431,40 @@ function checkRunReady() {
}
function openRelevantListing(state: EmuState) {
// if we clicked on another window, retain it
if (lastViewClicked != null) return;
// if we clicked on a specific tool, don't switch windows
if (lastViewClicked && lastViewClicked.startsWith('#')) return;
// don't switch windows for specific debug commands
if (['toline','restart','tovsync','stepover'].includes(lastDebugCommand)) return;
// has to support disassembly, at least
if (!platform.disassemble) return;
// search through listings
var listings = current_project.getListings();
var bestid = "#disasm";
var bestscore = 32;
let listings = current_project.getListings();
let bestid = "#disasm";
let bestscore = 256;
if (listings) {
var pc = state.c ? (state.c.EPC || state.c.PC) : 0;
for (var lstfn in listings) {
var lst = listings[lstfn];
var file = lst.assemblyfile || lst.sourcefile;
let pc = state.c ? (state.c.EPC || state.c.PC) : 0;
for (let lstfn in listings) {
let lst = listings[lstfn];
let file = lst.assemblyfile || lst.sourcefile;
// pick either listing or source file
var wndid = current_project.filename2path[lstfn] || lstfn;
let wndid = current_project.filename2path[lstfn] || lstfn;
if (file == lst.sourcefile) wndid = projectWindows.findWindowWithFilePrefix(lstfn);
// does this window exist?
if (projectWindows.isWindow(wndid)) {
var res = file && file.findLineForOffset(pc, 32); // TODO: const
if (res && pc-res.offset < bestscore) {
bestid = wndid;
bestscore = pc-res.offset;
// find the source line at the PC or closely before it
let srcline1 = file && file.findLineForOffset(pc, PC_LINE_LOOKAHEAD);
if (srcline1) {
// try to find the next line and bound the PC
let srcline2 = file.lines[srcline1.line+1];
if (!srcline2 || pc < srcline2.offset) {
let score = pc - srcline1.offset;
if (score < bestscore) {
bestid = wndid;
bestscore = score;
}
}
//console.log(hex(pc,4), srcline1, srcline2, wndid, lstfn, bestid, bestscore);
}
//console.log(hex(pc,4), wndid, lstfn, bestid, bestscore);
}
}
}
@ -1464,15 +1480,18 @@ function uiDebugCallback(state: EmuState) {
debugTickPaused = true;
}
function setupDebugCallback(btnid? : string) {
if (platform.setupDebug) platform.setupDebug((state:EmuState, msg:string) => {
uiDebugCallback(state);
setDebugButtonState(btnid||"pause", "stopped");
msg && showErrorAlert([{msg:"STOPPED: " + msg, line:0}], true);
});
function setupDebugCallback(btnid? : DebugCommandType) {
if (platform.setupDebug) {
platform.setupDebug((state:EmuState, msg:string) => {
uiDebugCallback(state);
setDebugButtonState(btnid||"pause", "stopped");
msg && showErrorAlert([{msg:"STOPPED: " + msg, line:0}], true);
});
lastDebugCommand = btnid;
}
}
function setupBreakpoint(btnid? : string) {
function setupBreakpoint(btnid? : DebugCommandType) {
if (!checkRunReady()) return;
_disableRecording();
setupDebugCallback(btnid);

View File

@ -304,12 +304,15 @@ export class MemoryMapView implements ProjectView {
// TODO: clear buffer when scrubbing
const OPAQUE_BLACK = 0xff000000;
export abstract class ProbeViewBaseBase {
probe : ProbeRecorder;
tooldiv : HTMLElement;
cumulativeData : boolean = false;
cyclesPerLine : number;
totalScanlines : number;
sp : number; // stack pointer
abstract tick() : void;
@ -369,15 +372,19 @@ export abstract class ProbeViewBaseBase {
var row=0;
var col=0;
var clk=0;
this.sp = 0;
for (var i=0; i<p.idx; i++) {
var word = p.buf[i];
var addr = word & 0xffff;
var value = (word >> 16) & 0xff;
var op = word & 0xff000000;
var op = word & OPAQUE_BLACK;
switch (op) {
case ProbeFlags.SCANLINE: row++; col=0; break;
case ProbeFlags.FRAME: row=0; col=0; break;
case ProbeFlags.CLOCKS: col += addr; clk += addr; break;
case ProbeFlags.SP_PUSH:
case ProbeFlags.SP_POP:
this.sp = addr;
default:
eventfn(op, addr, col, row, clk, value);
break;
@ -406,7 +413,7 @@ export abstract class ProbeViewBaseBase {
return s;
}
getOpRGB(op:number) : number {
getOpRGB(op:number, addr:number) : number {
switch (op) {
case ProbeFlags.EXECUTE: return 0x018001;
case ProbeFlags.MEM_READ: return 0x800101;
@ -415,7 +422,7 @@ export abstract class ProbeViewBaseBase {
case ProbeFlags.IO_WRITE: return 0xc00180;
case ProbeFlags.VRAM_READ: return 0x808001;
case ProbeFlags.VRAM_WRITE: return 0x4080c0;
case ProbeFlags.INTERRUPT: return 0xcfcfcf;
case ProbeFlags.INTERRUPT: return 0x3fbf3f;
case ProbeFlags.ILLEGAL: return 0x3f3fff;
default: return 0;
}
@ -519,14 +526,17 @@ abstract class ProbeBitmapViewBase extends ProbeViewBase {
refresh() {
this.tick();
this.datau32.fill(0xff000000);
this.datau32.fill(OPAQUE_BLACK);
}
tick() {
super.tick();
this.drawImage();
}
drawImage() {
this.ctx.putImageData(this.imageData, 0, 0);
}
clear() {
this.datau32.fill(0xff000000);
this.datau32.fill(OPAQUE_BLACK);
}
}
@ -541,17 +551,17 @@ export class AddressHeatMapView extends ProbeBitmapViewBase implements ProjectVi
var v = platform.readAddress(i);
var rgb = (v >> 2) | (v & 0x1f);
rgb |= (rgb<<8) | (rgb<<16);
this.datau32[i] = rgb | 0xff000000;
this.datau32[i] = rgb | OPAQUE_BLACK;
}
}
drawEvent(op, addr, col, row) {
var rgb = this.getOpRGB(op);
var rgb = this.getOpRGB(op, addr);
if (!rgb) return;
var x = addr & 0xff;
var y = (addr >> 8) & 0xff;
var data = this.datau32[addr & 0xffff];
data = data | rgb | 0xff000000;
data = data | rgb | OPAQUE_BLACK;
this.datau32[addr & 0xffff] = data;
}
@ -589,12 +599,62 @@ export class AddressHeatMapView extends ProbeBitmapViewBase implements ProjectVi
export class RasterPCHeatMapView extends ProbeBitmapViewBase implements ProjectView {
drawEvent(op, addr, col, row) {
var iofs = col + row * this.canvas.width;
var rgb = this.getOpRGB(op);
var rgb = this.getOpRGB(op, addr);
if (!rgb) return;
var data = this.datau32[iofs];
data = data | rgb | 0xff000000;
this.datau32[iofs] = data;
var iofs = col + row * this.canvas.width;
var data = rgb | OPAQUE_BLACK;
this.datau32[iofs] |= data;
}
drawImage() {
// fill in the gaps
let last = OPAQUE_BLACK;
for (let i=0; i<this.datau32.length; i++) {
if (this.datau32[i] == OPAQUE_BLACK) {
this.datau32[i] = last;
} else {
last = this.datau32[i];
}
}
super.drawImage();
}
}
export class RasterStackMapView extends RasterPCHeatMapView implements ProjectView {
interrupt: number = 0;
rgb: number = 0;
lastpc: number = 0;
drawEvent(op, addr, col, row) {
var iofs = col + row * this.canvas.width;
// track interrupts
if (op == ProbeFlags.INTERRUPT) this.interrupt = 1;
if (this.interrupt == 1 && op == ProbeFlags.SP_PUSH) this.interrupt = addr;
if (this.interrupt > 1 && this.sp > this.interrupt) this.interrupt = 0;
// track writes
if (op == ProbeFlags.MEM_WRITE) { this.rgb |= 0x00002f; }
if (op == ProbeFlags.VRAM_WRITE) { this.rgb |= 0x003f80; }
if (op == ProbeFlags.IO_WRITE) { this.rgb |= 0x1f3f80; }
if (op == ProbeFlags.IO_READ) { this.rgb |= 0x001f00; }
// draw pixels?
if (op == ProbeFlags.ILLEGAL) {
this.datau32[iofs] = 0xff0f0f0f;
} else {
let data = this.rgb;
if (op == ProbeFlags.EXECUTE) {
let sp = this.sp & 15;
if (sp >= 8) sp = 16-sp;
if (Math.abs(this.lastpc) - addr > 16) { sp += 1; }
if (Math.abs(this.lastpc) - addr > 256) { sp += 1; }
data = this.rgb = (0x080808 * sp) + 0x202020;
this.lastpc = addr;
}
if (this.interrupt) { data |= 0x800040; }
if (this.datau32[iofs] == OPAQUE_BLACK) {
this.datau32[iofs] = data | OPAQUE_BLACK;
}
}
}
}
@ -672,7 +732,7 @@ export class ScanlineIOView extends ProbeViewBaseBase {
var opaddr = line[i];
if (opaddr !== undefined) {
var addr = opaddr & 0xffff;
var op = op & 0xff000000;
var op = op & OPAQUE_BLACK;
if (op == ProbeFlags.EXECUTE) {
s += ',';
} else {

View File

@ -23,6 +23,9 @@ function createTextSpan(text:string, className:string) : HTMLElement {
/////
// look ahead this many bytes when finding source lines for a PC
export const PC_LINE_LOOKAHEAD = 64;
const MAX_ERRORS = 200;
const MODEDEFS = {
@ -384,7 +387,7 @@ export class SourceEditor implements ProjectView {
cpustate = platform.getCPUState();
if (cpustate) {
var EPC = (cpustate && (cpustate.EPC || cpustate.PC));
var res = this.sourcefile.findLineForOffset(EPC, 15);
var res = this.sourcefile.findLineForOffset(EPC, PC_LINE_LOOKAHEAD);
return res;
}
}
@ -598,7 +601,7 @@ export class ListingView extends DisassemblerView implements ProjectView {
var state = lastDebugState || platform.saveState();
var pc = state.c ? (state.c.EPC || state.c.PC) : 0;
if (pc >= 0 && this.assemblyfile) {
var res = this.assemblyfile.findLineForOffset(pc, 15);
var res = this.assemblyfile.findLineForOffset(pc, PC_LINE_LOOKAHEAD);
if (res) {
// set cursor while debugging
if (moveCursor) {

View File

@ -1,8 +1,7 @@
import { MOS6502, MOS6502State } from "../common/cpu/MOS6502";
import { BasicMachine, RasterFrameBased, Bus, ProbeAll, Probeable, NullProbe } from "../common/devices";
import { KeyFlags, newAddressDecoder, padBytes, Keys, makeKeycodeMap, newKeyboardHandler, EmuHalt, dumpRAM } from "../common/emu";
import { lzgmini, stringToByteArray, hex, rgb2bgr } from "../common/util";
import { AcceptsPaddleInput, Probeable } from "../common/devices";
import { dumpRAM, KeyFlags } from "../common/emu";
import { clamp, hex, lpad } from "../common/util";
// https://www.c64-wiki.com/wiki/C64
// http://www.zimmers.net/cbmpics/cbm/c64/vic-ii.txt
@ -16,7 +15,8 @@ import { Machine } from "../common/baseplatform";
import { TrapCondition } from "../common/devices";
import { BaseWASMMachine } from "../common/wasmplatform";
export class C64_WASMMachine extends BaseWASMMachine implements Machine, Probeable {
export class C64_WASMMachine extends BaseWASMMachine
implements Machine, Probeable, AcceptsPaddleInput {
numTotalScanlines = 312;
cpuCyclesPerLine = 63;
@ -24,6 +24,8 @@ export class C64_WASMMachine extends BaseWASMMachine implements Machine, Probeab
prgstart : number;
joymask0 = 0;
joymask1 = 0;
lightpen_x = 0;
lightpen_y = 0;
loadBIOS(srcArray: Uint8Array) {
var patch1ofs = 0xea24 - 0xe000 + 0x3000;
@ -34,7 +36,7 @@ export class C64_WASMMachine extends BaseWASMMachine implements Machine, Probeab
super.reset();
// clear keyboard
for (var ch=0; ch<128; ch++) {
this.setKeyInput(ch, 0, KeyFlags.KeyUp);
this.exports.machine_key_up(this.sys, ch);
}
// load rom
if (this.romptr && this.romlen) {
@ -50,12 +52,12 @@ export class C64_WASMMachine extends BaseWASMMachine implements Machine, Probeab
// advance BIOS a few frames
this.exports.machine_exec(this.sys, 250000);
// type in command (SYS 2061)
var cmd = "SYS "+this.prgstart+"\r";
var cmd = "\rSYS "+this.prgstart+"\r";
for (var i=0; i<cmd.length; i++) {
var key = cmd.charCodeAt(i);
this.exports.machine_exec(this.sys, 20000);
this.exports.machine_key_down(this.sys, key);
this.exports.machine_exec(this.sys, 20000);
this.exports.machine_exec(this.sys, 5000);
this.exports.machine_key_up(this.sys, key);
}
// advance clock until program starts
@ -78,12 +80,14 @@ export class C64_WASMMachine extends BaseWASMMachine implements Machine, Probeab
}
advanceFrame(trap: TrapCondition) : number {
// TODO: does this sync with VSYNC?
var scanline = this.exports.machine_get_raster_line(this.sys);
var clocks = Math.floor((this.numTotalScanlines - scanline) * (19656+295) / this.numTotalScanlines);
// TODO: ticks, not msec (machine_tick() has different rate then machine_exec())
var scanline = this.getRasterY();
var clocks = Math.floor((this.numTotalScanlines - scanline) * 19656 / this.numTotalScanlines);
var probing = this.probe != null;
if (probing) this.exports.machine_reset_probe_buffer();
clocks = super.advanceFrameClock(trap, clocks);
if (probing) this.copyProbeData();
//console.log(clocks, this.getRasterY());
return clocks;
}
getCPUState() {
@ -103,18 +107,26 @@ export class C64_WASMMachine extends BaseWASMMachine implements Machine, Probeab
V:s[10] & 64,
N:s[10] & 128,
o:this.readConst(pc),
R:s[19] != 0x37, // bit 28 of PINS
}
}
saveState() {
this.exports.machine_save_state(this.sys, this.stateptr);
/*
for (var i=0; i<this.statearr.length; i++)
if (this.statearr[i] == 0xa0 && this.statearr[i+1] == 0x4d && this.statearr[i+2] == 0xe2) console.log(hex(i));
*/
let cia1 = this.getDebugStateOffset(1);
let cia2 = this.getDebugStateOffset(2);
let vic = this.getDebugStateOffset(3);
let sid = this.getDebugStateOffset(4);
let ramofs = this.getDebugStateOffset(5);
let pla = this.getDebugStateOffset(9);
return {
c:this.getCPUState(),
state:this.statearr.slice(0),
ram:this.statearr.slice(18640, 18640+0x200), // ZP and stack
ram:this.statearr.slice(ramofs, ramofs+0x10000),
cia1:this.statearr.slice(cia1, cia1+64),
cia2:this.statearr.slice(cia2, cia2+64),
vic:this.statearr.slice(vic+1, vic+1+64),
sid:this.statearr.slice(sid, sid+32),
pla:this.statearr.slice(pla, pla+16)
};
}
loadState(state) : void {
@ -122,7 +134,7 @@ export class C64_WASMMachine extends BaseWASMMachine implements Machine, Probeab
this.exports.machine_load_state(this.sys, this.stateptr);
}
getVideoParams() {
return {width:392, height:272, overscan:true, videoFrequency:50};
return {width:392, height:272, overscan:true, videoFrequency:50, aspect:392/272*0.9365};
}
setKeyInput(key: number, code: number, flags: number): void {
// TODO: handle shifted keys
@ -132,22 +144,36 @@ export class C64_WASMMachine extends BaseWASMMachine implements Machine, Probeab
// convert to c64
var mask = 0;
var mask2 = 0;
if (key == 37) { key = 0x8; mask = 0x4; } // LEFT
if (key == 38) { key = 0xb; mask = 0x1; } // UP
if (key == 39) { key = 0x9; mask = 0x8; } // RIGHT
if (key == 40) { key = 0xa; mask = 0x2; } // DOWN
if (key == 32) { mask = 0x10; } // FIRE
/* player 2 (TODO)
if (key == 65) { key = 65; mask2 = 0x4; } // LEFT
if (key == 87) { key = 87; mask2 = 0x1; } // UP
if (key == 68) { key = 68; mask2 = 0x8; } // RIGHT
if (key == 83) { key = 83; mask2 = 0x2; } // DOWN
if (key == 69) { mask2 = 0x10; } // FIRE
*/
if (key == 113) { key = 0xf1; } // F2
if (key == 115) { key = 0xf3; } // F4
if (key == 119) { key = 0xf5; } // F8
if (key == 121) { key = 0xf7; } // F10
switch (key) {
case 32: mask = 0x10; break;
case 37: key = 0x8; mask = 0x4; break; // LEFT
case 38: key = 0xb; mask = 0x1; break; // UP
case 39: key = 0x9; mask = 0x8; break; // RIGHT
case 40: key = 0xa; mask = 0x2; break; // DOWN
case 113: key = 0xf1; break; // F2
case 115: key = 0xf3; break; // F4
case 119: key = 0xf5; break; // F8
case 121: key = 0xf7; break; // F10
case 188: key = flags & KeyFlags.Shift ? 0x3c : 0x2e; break; // < .
case 190: key = flags & KeyFlags.Shift ? 0x3e : 0x2c; break; // > ,
case 191: key = flags & KeyFlags.Shift ? 0x3f : 0x2f; break; // ? /
case 222: key = flags & KeyFlags.Shift ? 0x22 : 0x27; break; // " '
case 219: key = flags & KeyFlags.Shift ? 0x7b : 0x5b; break; // [
case 221: key = flags & KeyFlags.Shift ? 0x7d : 0x5d; break; // ]
case 48: if (flags & KeyFlags.Shift) key = 0x29; break; // )
case 49: if (flags & KeyFlags.Shift) key = 0x21; break; // !
case 50: if (flags & KeyFlags.Shift) key = 0x40; break; // @
case 51: if (flags & KeyFlags.Shift) key = 0x23; break; // #
case 52: if (flags & KeyFlags.Shift) key = 0x24; break; // $
case 53: if (flags & KeyFlags.Shift) key = 0x25; break; // %
case 54: if (flags & KeyFlags.Shift) key = 0x5e; break; // ^
case 55: if (flags & KeyFlags.Shift) key = 0x26; break; // &
case 56: if (flags & KeyFlags.Shift) key = 0x2a; break; // *
case 57: if (flags & KeyFlags.Shift) key = 0x28; break; // (
case 59: if (flags & KeyFlags.Shift) key = 0x3a; break; // ;
case 61: if (flags & KeyFlags.Shift) key = 0x2b; break; // +
case 173: key = flags & KeyFlags.Shift ? 0x5f : 0x2d; break; // _ -
}
if (flags & KeyFlags.KeyDown) {
this.exports.machine_key_down(this.sys, key);
this.joymask0 |= mask;
@ -159,5 +185,87 @@ export class C64_WASMMachine extends BaseWASMMachine implements Machine, Probeab
}
this.exports.c64_joystick(this.sys, this.joymask0, this.joymask1);
}
getRasterY() {
return this.exports.machine_get_raster_line(this.sys);
}
getDebugStateOffset(index: number) {
var p = this.exports.machine_get_debug_pointer(this.sys, index);
return p - this.sys;
}
getDebugCategories() {
return ['CPU','ZPRAM','Stack','PLA','CIA','VIC','SID'];
}
getDebugInfo(category:string, state:any) {
switch (category) {
case 'PLA': {
let s = "";
let iomapped = state.pla[0];
let port = state.pla[3];
s += `$0000 - $9FFF RAM\n`;
s += `$A000 - $BFFF ${(port&3)==3 ? 'BASIC ROM' : 'RAM'}\n`;
s += `$C000 - $CFFF RAM\n`;
s += `$D000 - $DFFF ${iomapped ? 'I/O' : (port&3)!=0 ? 'CHAR ROM' : 'RAM'}\n`;
s += `$E000 - $FFFF ${(port&2)==2 ? 'KERNAL ROM' : 'RAM'}\n`;
return s;
}
case 'CIA': {
let s = "";
for (let i=0; i<2; i++) {
let m = i ? state.cia2 : state.cia1;
s += `CIA ${i+1}\n`;
s += ` A: Data ${hex(m[0])} DDR ${hex(m[1])} Input ${hex(m[2])}`;
s += ` Timer ${hex(m[10]+m[11]*256, 4)}\n`;
s += ` B: Data ${hex(m[4])} DDR ${hex(m[5])} Input ${hex(m[6])}`;
s += ` Timer ${hex(m[10+10]+m[11+10]*256, 4)}\n`;
//s += ` IMR ${hex(m[48])} ICR ${hex(m[50])}\n`
}
return s;
}
case 'VIC': {
let m = state.vic;
let s = '';
let vicbank = ((state.cia2[0] & 3) ^ 3) * 0x4000;
let charmem = vicbank + (state.vic[0x18] & 14) * 0x400;
let screen = vicbank + (state.vic[0x18] >> 4) * 0x400;
let isbitmap = state.vic[0x11] & 0x20;
let ischar = (state.cia2[0]&1)==1 && (state.vic[0x18]&12)==4;
let rasterX = state.state[0xf4];
let rasterY = this.getRasterY();
s += 'Mode:';
if (state.vic[0x11] & 0x20) s += ' BITMAP'; else s += ' CHAR';
if (state.vic[0x16] & 0x10) s += ' MULTICOLOR';
if (state.vic[0x11] & 0x40) s += ' EXTENDED';
s += "\n";
s += `Raster: (${lpad(rasterX,3)}, ${lpad(rasterY,3)}) `;
s += `Scroll: (${state.vic[0x16] & 7}, ${state.vic[0x11] & 7})`;
s += "\n";
s += `VIC Bank: $${hex(vicbank,4)} Scrn: $${hex(screen,4)} `;
if (isbitmap) s += `Bitmap: $${hex(charmem&0xe000,4)}`
else if (ischar) s += `Char: ROM $${hex(charmem,4)}`;
else s += `Char: $${hex(charmem,4)}`;
s += "\n";
s += dumpRAM(m, 0xd000, 64);
return s;
}
case 'SID': {
let m = state.sid;
let s = ''
s += dumpRAM(m, 0xd400, 32);
return s;
}
}
}
setPaddleInput(controller: number, value: number): void {
if (controller == 0) this.lightpen_x = value;
if (controller == 1) this.lightpen_y = value;
const x1 = 22;
const y1 = 36;
const x2 = 228;
const y2 = 220;
let x = clamp(0, 255, (this.lightpen_x - x1) / (x2 - x1) * 160 + 24);
let y = clamp(0, 255, (this.lightpen_y - y1) / (y2 - y1) * 200 + 50);
this.exports.machine_set_mouse(this.sys, x, y);
}
}

View File

@ -58,14 +58,17 @@ export class CPC_WASMMachine extends BaseWASMMachine implements Machine {
}
}
advanceFrame(trap: TrapCondition) : number {
var scanline = this.exports.machine_get_raster_line(this.sys);
var clocks = Math.floor((this.numTotalScanlines - scanline) * 19965 / this.numTotalScanlines);
var scanline = this.getRasterY();
var clocks = Math.floor((this.numTotalScanlines - scanline) * (4000000/50) / this.numTotalScanlines);
var probing = this.probe != null;
if (probing) this.exports.machine_reset_probe_buffer();
clocks = super.advanceFrameClock(trap, clocks);
if (probing) this.copyProbeData();
return clocks;
}
getRasterY() {
return this.exports.machine_get_raster_line(this.sys);
}
/*
z80_tick_t tick_cb; // 0
uint64_t bc_de_hl_fa; // 8

View File

@ -73,14 +73,17 @@ export class VIC20_WASMMachine extends BaseWASMMachine implements Machine, Probe
}
advanceFrame(trap: TrapCondition) : number {
// TODO: does this sync with VSYNC?
var scanline = this.exports.machine_get_raster_line(this.sys);
var clocks = Math.floor((this.numTotalScanlines - scanline) * (19656+295+32) / this.numTotalScanlines);
var scanline = this.getRasterY();
var clocks = Math.floor((this.numTotalScanlines - scanline) * 19656 / this.numTotalScanlines);
var probing = this.probe != null;
if (probing) this.exports.machine_reset_probe_buffer();
clocks = super.advanceFrameClock(trap, clocks);
if (probing) this.copyProbeData();
return clocks;
}
getRasterY() {
return this.exports.machine_get_raster_line(this.sys);
}
getCPUState() {
this.exports.machine_save_cpu_state(this.sys, this.cpustateptr);
var s = this.cpustatearr;

View File

@ -20,7 +20,7 @@ export class ZX_WASMMachine extends BaseWASMMachine implements Machine {
reset() {
super.reset();
// advance bios
this.exports.machine_exec(this.sys, 500000); // TODO?
this.exports.machine_exec(this.sys, 2000000);
// load rom (Z80 header: https://worldofspectrum.org/faq/reference/z80format.htm)
if (this.romptr && this.romlen) {
// TODO
@ -41,7 +41,7 @@ export class ZX_WASMMachine extends BaseWASMMachine implements Machine {
//var scanline = this.exports.machine_get_raster_line(this.sys);
var probing = this.probe != null;
if (probing) this.exports.machine_reset_probe_buffer();
var clocks = super.advanceFrameClock(trap, Math.floor(1000000 / 50)); // TODO: use ticks, not msec
var clocks = super.advanceFrameClock(trap, Math.floor(3500000 / 50));
if (probing) this.copyProbeData();
return clocks;
}

View File

@ -6,28 +6,39 @@ import { BaseMAME6502Platform } from "../common/mameplatform";
const C64_PRESETS = [
{id:'hello.dasm', name:'Hello World (ASM)'},
{id:'eliza.c', name:'Eliza (C)'},
{id:'tgidemo.c', name:'TGI Graphics Demo (C)'},
{id:'upandaway.c', name:'Up, Up and Away (C)'},
{id:'joymove.c', name:'Joystick Movement (C)'},
{id:'siegegame.c', name:'Siege Game (C)'},
{id:'scroll1.c', name:'Scrolling 1 (C)'},
{id:'scroll2.c', name:'Scrolling 2 (C)'},
{id:'scroll3.c', name:'Scrolling 3 (C)'},
{id:'scroll4.c', name:'Scrolling 4 (C)'},
{id:'scroll5.c', name:'Scrolling 5 (C)'},
{id:'climber.c', name:'Climber Game (C)'},
{id:'multilines.c', name:'Multicolor Lines+Flood Fill (C)'},
{id:'sidtune.dasm', name:'SID Tune (ASM)'},
{id:'musicplayer.c', name:'Music Player (C)'},
{id:'23matches.c', name:'23 Matches'},
{id:'tgidemo.c', name:'TGI Graphics Demo'},
{id:'upandaway.c', name:'Up, Up and Away'},
{id:'siegegame.c', name:'Siege Game'},
{id:'joymove.c', name:'Sprite Movement'},
{id:'sprite_collision.c', name:'Sprite Collision'},
{id:'scroll1.c', name:'Scrolling (Single Buffer)'},
{id:'scroll2.c', name:'Scrolling (Double Buffer)'},
{id:'scroll3.c', name:'Scrolling (Multidirectional)'},
{id:'scroll4.c', name:'Scrolling (Color RAM Buffering)'},
{id:'scroll5.c', name:'Scrolling (Camera Following)'},
{id:'side_scroller.c', name:'Side-Scrolling Game'},
{id:'fullscrollgame.c', name:'Full-Scrolling Game'},
{id:'test_multiplex.c', name:'Sprite Retriggering'},
{id:'test_multispritelib.c', name:'Sprite Multiplexing Library'},
{id:'scrolling_text.c', name:'Big Scrolling Text'},
{id:'mcbitmap.c', name:'Multicolor Bitmap Mode'},
//{id:'mandel.c', name:'Mandelbrot Fractal'},
{id:'musicplayer.c', name:'Music Player'},
//{id:'sidtune.dasm', name:'Tiny SID Tune (ASM)'},
{id:'siddemo.c', name:'SID Player Demo'},
{id:'climber.c', name:'Climber Game'},
];
const C64_MEMORY_MAP = { main:[
{name:'6510 Registers',start:0x0, size:0x2,type:'io'},
{name:'BIOS Reserved', start:0x200, size:0xa7},
{name:'Default Screen RAM', start:0x400, size:1024,type:'ram'},
//{name:'RAM', start:0x2, size:0x7ffe,type:'ram'},
{name:'Cartridge ROM',start:0x8000,size:0x2000,type:'rom'},
{name:'BASIC ROM', start:0xa000,size:0x2000,type:'rom'},
{name:'RAM', start:0xc000,size:0x1000,type:'ram'},
{name:'Upper RAM', start:0xc000,size:0x1000,type:'ram'},
{name:'Character ROM',start:0xd000,size:0x1000,type:'rom'},
{name:'VIC-II I/O', start:0xd000,size:0x0400,type:'io'},
{name:'SID', start:0xd400,size:0x0400,type:'io'},
{name:'Color RAM', start:0xd800,size:0x0400,type:'io'},

View File

@ -138,6 +138,7 @@ class JSNESPlatform extends Base6502Platform implements Platform, Probeable {
// insert debug hook
this.nes.cpu._emulate = this.nes.cpu.emulate;
this.nes.cpu.emulate = () => {
if (this.nes.cpu.irqRequested) this.probe.logInterrupt(this.nes.cpu.irqType || 0);
this.probe.logExecute(this.nes.cpu.REG_PC+1, this.nes.cpu.REG_SP);
var cycles = this.nes.cpu._emulate();
this.evalDebugCondition();

View File

@ -394,7 +394,8 @@ class VCSPlatform extends BasePlatform {
bus.oldWrite = bus.write;
bus.write = function(a,v) {
this.oldWrite(a,v);
if (a < 0x80) probe.logIOWrite(a,v);
if (a == 0x02) probe.logIllegal(a); // WSYNC
else if (a < 0x80) probe.logIOWrite(a,v);
else if (a > 0x280 && a < 0x300) probe.logIOWrite(a,v);
else probe.logWrite(a,v);
}

View File

@ -1 +1 @@
{"files":[{"filename":"/common/snes/icd2.wiz","start":0,"end":2959},{"filename":"/common/snes/img2snes.py","start":2959,"end":5920},{"filename":"/common/snes/minirpg_sprites.png","start":5920,"end":7519},{"filename":"/common/snes/hello_tiles_compact.png","start":7519,"end":8122},{"filename":"/common/snes/hello_tiles_compact.chr","start":8122,"end":9658},{"filename":"/common/snes/hello_tiles.png","start":9658,"end":10295},{"filename":"/common/snes/hello_tiles.chr","start":10295,"end":18487},{"filename":"/common/snes/snes.wiz","start":18487,"end":42074},{"filename":"/common/snes/minirpg_sprites.chr","start":42074,"end":50266},{"filename":"/common/gg/sms.wiz","start":50266,"end":56495},{"filename":"/common/gg/minirpg_sprites.png","start":56495,"end":58094},{"filename":"/common/gg/hello_tiles.png","start":58094,"end":58731},{"filename":"/common/gg/img2gg8.py","start":58731,"end":61609},{"filename":"/common/gg/hello_tiles.chr","start":61609,"end":69801},{"filename":"/common/gg/img2gg16.py","start":69801,"end":73816},{"filename":"/common/gg/minirpg_sprites.chr","start":73816,"end":82008},{"filename":"/common/nes/scroller_tiles.chr","start":82008,"end":86104},{"filename":"/common/nes/minirpg_sprites.png","start":86104,"end":87701},{"filename":"/common/nes/hello_tiles.png","start":87701,"end":88138},{"filename":"/common/nes/img2chr.py","start":88138,"end":90528},{"filename":"/common/nes/hello_tiles.chr","start":90528,"end":94624},{"filename":"/common/nes/scroller_tiles.png","start":94624,"end":95467},{"filename":"/common/nes/bobble_tiles.png","start":95467,"end":96149},{"filename":"/common/nes/nes.wiz","start":96149,"end":115238},{"filename":"/common/nes/bobble_tiles.chr","start":115238,"end":119334},{"filename":"/common/nes/minirpg_sprites.chr","start":119334,"end":123430},{"filename":"/common/msx/img2msx16.py","start":123430,"end":125505},{"filename":"/common/msx/hello_tiles.png","start":125505,"end":125998},{"filename":"/common/msx/hello_tiles.chr","start":125998,"end":128046},{"filename":"/common/msx/img2msx8.py","start":128046,"end":130360},{"filename":"/common/msx/msx.wiz","start":130360,"end":185578},{"filename":"/common/pce/pce.wiz","start":185578,"end":198258},{"filename":"/common/pce/minirpg_sprites.png","start":198258,"end":199857},{"filename":"/common/pce/hello_tiles.png","start":199857,"end":200494},{"filename":"/common/pce/hello_tiles.chr","start":200494,"end":208686},{"filename":"/common/pce/img2pce8.py","start":208686,"end":211606},{"filename":"/common/pce/minirpg_sprites.chr","start":211606,"end":219798},{"filename":"/common/gb/bcd.wiz","start":219798,"end":222637},{"filename":"/common/gb/joy.wiz","start":222637,"end":225456},{"filename":"/common/gb/gb.wiz","start":225456,"end":242454},{"filename":"/common/gb/oam.wiz","start":242454,"end":243780},{"filename":"/common/gb/math.wiz","start":243780,"end":246131},{"filename":"/common/gb/sgb_util.wiz","start":246131,"end":249318},{"filename":"/common/gb/memory.wiz","start":249318,"end":250760},{"filename":"/common/gb/gbc_util.wiz","start":250760,"end":251810},{"filename":"/common/2600/vcs.wiz","start":251810,"end":257710},{"filename":"/common/spc/spc.wiz","start":257710,"end":264462}],"remote_package_size":264462,"package_uuid":"f357339c-3604-4f7c-b827-41c09e93a3b5"}
{"files":[{"filename":"/common/snes/icd2.wiz","start":0,"end":2959},{"filename":"/common/snes/img2snes.py","start":2959,"end":5920},{"filename":"/common/snes/minirpg_sprites.png","start":5920,"end":7519},{"filename":"/common/snes/hello_tiles_compact.png","start":7519,"end":8122},{"filename":"/common/snes/hello_tiles_compact.chr","start":8122,"end":9658},{"filename":"/common/snes/hello_tiles.png","start":9658,"end":10295},{"filename":"/common/snes/hello_tiles.chr","start":10295,"end":18487},{"filename":"/common/snes/snes.wiz","start":18487,"end":42074},{"filename":"/common/snes/minirpg_sprites.chr","start":42074,"end":50266},{"filename":"/common/gg/sms.wiz","start":50266,"end":56495},{"filename":"/common/gg/minirpg_sprites.png","start":56495,"end":58094},{"filename":"/common/gg/hello_tiles.png","start":58094,"end":58731},{"filename":"/common/gg/img2gg8.py","start":58731,"end":61609},{"filename":"/common/gg/hello_tiles.chr","start":61609,"end":69801},{"filename":"/common/gg/img2gg16.py","start":69801,"end":73816},{"filename":"/common/gg/minirpg_sprites.chr","start":73816,"end":82008},{"filename":"/common/nes/scroller_tiles.chr","start":82008,"end":86104},{"filename":"/common/nes/minirpg_sprites.png","start":86104,"end":87701},{"filename":"/common/nes/hello_tiles.png","start":87701,"end":88138},{"filename":"/common/nes/img2chr.py","start":88138,"end":90528},{"filename":"/common/nes/hello_tiles.chr","start":90528,"end":94624},{"filename":"/common/nes/scroller_tiles.png","start":94624,"end":95467},{"filename":"/common/nes/bobble_tiles.png","start":95467,"end":96149},{"filename":"/common/nes/nes.wiz","start":96149,"end":115238},{"filename":"/common/nes/bobble_tiles.chr","start":115238,"end":119334},{"filename":"/common/nes/minirpg_sprites.chr","start":119334,"end":123430},{"filename":"/common/msx/img2msx16.py","start":123430,"end":125505},{"filename":"/common/msx/hello_tiles.png","start":125505,"end":125998},{"filename":"/common/msx/hello_tiles.chr","start":125998,"end":128046},{"filename":"/common/msx/img2msx8.py","start":128046,"end":130360},{"filename":"/common/msx/msx.wiz","start":130360,"end":185578},{"filename":"/common/pce/pce.wiz","start":185578,"end":198258},{"filename":"/common/pce/minirpg_sprites.png","start":198258,"end":199857},{"filename":"/common/pce/hello_tiles.png","start":199857,"end":200494},{"filename":"/common/pce/hello_tiles.chr","start":200494,"end":208686},{"filename":"/common/pce/img2pce8.py","start":208686,"end":211606},{"filename":"/common/pce/minirpg_sprites.chr","start":211606,"end":219798},{"filename":"/common/gb/bcd.wiz","start":219798,"end":222637},{"filename":"/common/gb/joy.wiz","start":222637,"end":225456},{"filename":"/common/gb/gb.wiz","start":225456,"end":242454},{"filename":"/common/gb/oam.wiz","start":242454,"end":243780},{"filename":"/common/gb/math.wiz","start":243780,"end":246131},{"filename":"/common/gb/sgb_util.wiz","start":246131,"end":249318},{"filename":"/common/gb/memory.wiz","start":249318,"end":250760},{"filename":"/common/gb/gbc_util.wiz","start":250760,"end":251810},{"filename":"/common/2600/vcs.wiz","start":251810,"end":257710},{"filename":"/common/spc/spc.wiz","start":257710,"end":264462}],"remote_package_size":264462,"package_uuid":"3682f967-645c-4761-b3e6-5a07a4ad8a35"}

View File

@ -119,9 +119,9 @@ export function assembleCA65(step: BuildStep): BuildStepResult {
}
execMain(step, CA65, args);
if (errors.length) {
// TODO?
let listings : CodeListingMap = {};
listings[step.path] = { lines:[], text:getWorkFileAsString(step.path) };
// TODO? change extension to .lst
//listings[step.path] = { lines:[], text:getWorkFileAsString(step.path) };
return { errors, listings };
}
objout = FS.readFile(objpath, { encoding: 'binary' });

Binary file not shown.

View File

@ -1188,6 +1188,7 @@ var TOOL_PRELOADFS = {
'wiz': 'wiz',
'ecs-vcs': '65-none', // TODO: support multiple platforms
'ecs-nes': '65-nes', // TODO: support multiple platforms
'ecs-c64': '65-c64', // TODO: support multiple platforms
}
//const waitFor = delay => new Promise(resolve => setTimeout(resolve, delay)); // for testing

View File

@ -44,6 +44,7 @@ async function doBuild(msgs, callback, outlen, nlines, nerrors, options) {
}
assert.ok(err.msg);
}
if (nerrors != msg.errors.length) console.log(msg);
assert.equal(nerrors, msg.errors.length);
} else {
assert.equal(nerrors||0, 0);
@ -242,7 +243,8 @@ describe('Worker', function() {
});
it('should compile C64 cc65 skeleton', function(done) {
var csource = ab2str(fs.readFileSync('presets/c64/skeleton.cc65'));
compile('cc65', csource, 'c64.wasm', done, 2663, 2, 0);
csource = csource.replace('#include "','//');
compile('cc65', csource, 'c64.wasm', done, 3001, 3, 0);
});
it('should compile zmachine inform6 skeleton', function(done) {
var csource = ab2str(fs.readFileSync('presets/zmachine/skeleton.inform6'));